import mplcursors
import time
import akshare as ak
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta, date
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np
import threading
import re
from tkcalendar import Calendar, DateEntry
from dateutil.relativedelta import relativedelta
import requests
from bs4 import BeautifulSoup
import os
from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk
import traceback
from matplotlib.dates import DateFormatter
from datetime import datetime

from date_utils import AShareTradingDayChecker, DeliveryDateCalculator
from components import DatePicker, Logger
from data_fetcher import data_fetcher


# 设置中文字体和后端
matplotlib.use("TkAgg")
plt.rcParams["font.family"] = ["SimHei"]  # 仅保留存在的字体
plt.rcParams["axes.unicode_minus"] = False
    
class StockAnalysisApp:
    def __init__(self, root):
        self.root = root
        self.root.title("A股资金流向与指数/成交额分析")
        self.root.geometry("1200x800")
        self.root.minsize(1000, 700)

        
        
        # 数据缓存
        self.current_data = None
        self.turnover_data = None
        self.main_data = None
        self.north_data = None
        self.turnover_cache = {}
        self.cache_days_limit = 3650
        self.delivery_analysis_cache = {}

        # 新增临时数据缓存
        self.temp_data_cache = {}

        # 添加成交额统计变量
        self.max_turnover = tk.StringVar()
        self.min_turnover = tk.StringVar()
        self.avg_turnover = tk.StringVar()
        self.total_turnover = tk.StringVar()

        #调试
        self.data_initialized = False
        self.data_last_update = None
        self.data_source = "未初始化"
        
        # 初始化默认值
        self.max_turnover.set("--")
        self.min_turnover.set("--")
        self.avg_turnover.set("--")
        self.total_turnover.set("--")

        self.max_inflow = tk.StringVar(value="最大净流入: --")
        self.max_outflow = tk.StringVar(value="最大净流出: --")
        self.avg_inflow = tk.StringVar(value="平均净流入: --")
        self.total_inflow = tk.StringVar(value="总净流入: --")
        
        self.max_turnover = tk.StringVar(value="最大成交额: --")
        self.min_turnover = tk.StringVar(value="最小成交额: --")
        self.avg_turnover = tk.StringVar(value="平均成交额: --")
        self.total_turnover = tk.StringVar(value="总成交额: --")

        self.main_positive_days = tk.StringVar(value="净流入正天数: --")
        self.main_monthly_positive = tk.StringVar(value="月均正天数: --")

        self.main_max_inflow = tk.StringVar()
        self.main_max_outflow = tk.StringVar()
        self.main_avg_inflow = tk.StringVar()
        self.main_total_inflow = tk.StringVar()
        self.turnover_max = tk.StringVar()
        self.turnover_min = tk.StringVar()
        self.turnover_avg = tk.StringVar()
        self.turnover_total = tk.StringVar()
        self.turnover_current = tk.StringVar()

         # 新增资金比率相关性变量
        self.main_ratio_value = tk.StringVar(value="--")
        self.north_ratio_value = tk.StringVar(value="--")

        

        # 初始化存储变量
        self.correlations = {
            # 修改为资金/成交额比值相关性
            'main_ratio': None,
            'north_ratio': None,
            'fund': None,
            'turnover': None
        }
        
        # 初始化显示变量
        self.main_value = tk.StringVar(value="--")
        self.north_value = tk.StringVar(value="--")
        
        self.fund_value = tk.StringVar(value="--")

        self.turnover_value = tk.StringVar(value="--")  # 确保这个变量存在
        # 新增比值相关性显示变量
        self.main_ratio_value = tk.StringVar(value="--")
        self.north_ratio_value = tk.StringVar(value="--")

        
        # 控制变量
        self.north_enabled = tk.BooleanVar(value=False)
        self.trading_day_checker = AShareTradingDayChecker()
        self.delivery_calculator = DeliveryDateCalculator(self.trading_day_checker)

        trading_day_checker = AShareTradingDayChecker()  # 需要先创建交易日检查器
        self.data_fetcher = data_fetcher(cache_dir="stock_cache", trading_day_checker=trading_day_checker)

        self.index_update_lock = False  # 防止重复更新
        self.last_index_params = None   # 缓存最后使用的指数参数
        
        # 自动更新相关
        self.update_job = None
        self.last_update_params = None

        # 第一步：初始化日志系统
        self.logger = Logger()  # 先初始化logger
        
        # 第二步：检查akshare版本（需要logger已存在）
        #self.check_akshare_version()
        

        self.index_update_job = None  # 指数更新任务
        self.fund_update_job = None   # 新增：资金流向更新任务
        self.turnover_update_job = None  # 成交额更新任务

        self.delivery_update_job = None
        self.last_delivery_params = None

        self.turnover_analysis_running = False  # 成交额分析状态

        # 缓存更新状态
        self.cache_updating = False
        self.cache_update_window = None
        self.cache_progress = None
        self.cache_log_text = None
        self.cache_update_thread = None
  

        ###
        # 在初始化后立即启动缓存更新
        #self.root.after(100, self.start_cache_update)
        
        # 缓存更新状态
        self.cache_updating = False
        self.cache_update_window = None
        self.cache_progress = None
        self.cache_log_text = None
        self.cache_update_thread = None
        ###

        self.fund_analysis_running = False
        
        # 初始化界面
        self.create_widgets()
        #self.root.after(100, self.initial_load)
        self.root.after(100, self.start_cache_update)
        self.create_overview_cards()  # 确保调用
        self.setup_autoupdate()
        
        #self.check_akshare_version()
        
        # 文件缓存
        self.cache_dir = "stock_cache"
        if not os.path.exists(self.cache_dir):
            os.makedirs(self.cache_dir)
            
        # 工具提示数据缓存
        self.tooltip_data = None
        self.turnover_cache_path = os.path.join(self.cache_dir, "turnover_cache.csv") 
        # 初始加载



    def start_cache_update(self):
        """启动缓存更新流程"""
        if self.cache_updating:
            return
            
        self.cache_updating = True
        self.show_cache_update_window()
        
        # 在后台线程执行缓存更新
        self.cache_update_thread = threading.Thread(
            target=self.execute_cache_update,
            daemon=True
        )
        self.cache_update_thread.start()

    def show_cache_update_window(self):
        """显示缓存更新窗口"""
        self.cache_update_window = tk.Toplevel(self.root)
        self.cache_update_window.title("数据缓存更新")
        self.cache_update_window.geometry("600x400")
        self.cache_update_window.resizable(False, False)
        self.cache_update_window.protocol("WM_DELETE_WINDOW", self.cancel_cache_update)
        self.cache_update_window.transient(self.root)  # 设为root的子窗口
        
        # 标题
        title_frame = ttk.Frame(self.cache_update_window)
        title_frame.pack(fill=tk.X, padx=10, pady=10)
        ttk.Label(title_frame, text="正在增量更新数据缓存...", font=("SimHei", 14)).pack(pady=5)
        
        # 进度条
        progress_frame = ttk.Frame(self.cache_update_window)
        progress_frame.pack(fill=tk.X, padx=20, pady=5)
        self.cache_progress = ttk.Progressbar(
            progress_frame, 
            orient=tk.HORIZONTAL, 
            length=550, 
            mode='determinate'
        )
        self.cache_progress.pack(fill=tk.X)
        
        # 日志区域
        log_frame = ttk.LabelFrame(self.cache_update_window, text="更新日志", padding=10)
        log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        self.cache_log_text = scrolledtext.ScrolledText(
            log_frame, 
            wrap=tk.WORD,
            state=tk.DISABLED
        )
        self.cache_log_text.pack(fill=tk.BOTH, expand=True)
        
        # 按钮区域
        btn_frame = ttk.Frame(self.cache_update_window)
        btn_frame.pack(fill=tk.X, padx=10, pady=5)
        
        self.cancel_btn = ttk.Button(
            btn_frame, 
            text="取消更新", 
            command=self.cancel_cache_update
        )
        self.cancel_btn.pack(side=tk.RIGHT, padx=5)

    def log_to_cache_window(self, message):
        """向缓存更新窗口添加日志"""
        if not self.cache_update_window or not self.cache_log_text:
            return
            
        self.root.after(0, lambda: self._add_log_message(message))

    def update_cache_progress(self, value, message=None):
        """更新缓存更新进度"""
        if not self.cache_update_window or not self.cache_progress:
            return
            
        if message:
            self.log_to_cache_window(message)
            
        self.root.after(0, lambda: self.cache_progress.configure(value=value))

    def execute_cache_update(self):
        """执行缓存更新（在后台线程中）"""
        try:
            # 更新进度和日志
            self.update_cache_progress(0, "开始增量更新数据缓存...")
            
            # 创建缓存目录（如果不存在）
            if not os.path.exists(self.cache_dir):
                os.makedirs(self.cache_dir)
                self.log_to_cache_window(f"创建缓存目录: {self.cache_dir}")
            
            # 定义更新任务
            update_tasks = [
                ("指数数据", self.incremental_update_index_cache),
                ("主力资金", self.incremental_update_main_money_cache),
                ("北向资金", self.incremental_update_north_money_cache),
                ("成交额数据", self.incremental_update_turnover_cache)
            ]
            
            # 执行所有更新任务
            total_tasks = len(update_tasks)
            for i, (task_name, task_func) in enumerate(update_tasks):
                if not self.cache_updating:
                    break
                    
                progress = (i / total_tasks) * 100
                self.update_cache_progress(progress, f"正在增量更新 {task_name}...")
                
                try:
                    task_func()
                    self.log_to_cache_window(f"{task_name}增量更新完成")
                except Exception as e:
                    self.log_to_cache_window(f"{task_name}增量更新失败: {str(e)}")
                    self.log_to_cache_window(traceback.format_exc())
                
                time.sleep(0.5)  # 任务间延迟
            
            # 更新完成
            if self.cache_updating:
                self.update_cache_progress(100, "所有数据缓存增量更新完成！")
                self.log_to_cache_window("准备加载数据进行分析...")
                
                # 关闭更新窗口并开始分析
                self.root.after(2000, self.finish_cache_update)
            else:
                self.log_to_cache_window("缓存更新已取消")
                
        except Exception as e:
            self.log_to_cache_window(f"缓存增量更新过程中发生错误: {str(e)}")
            self.log_to_cache_window(traceback.format_exc())
        finally:
            self.cache_updating = False

    def finish_cache_update(self):
        """完成缓存更新流程"""
        if self.cache_update_window:
            self.cache_update_window.destroy()
            self.cache_update_window = None
        
        # 开始加载数据进行分析
        self.initial_load()

    def update_index_cache(self):
        """更新所有指数缓存数据"""
        index_codes = [
            ("上证指数", "sh000001"),
            ("深证成指", "sz399001"),
            ("创业板指", "sz399006"),
            ("科创50", "sh000688"),
            ("沪深300", "sh000300")
        ]
        
        for name, code in index_codes:
            if not self.cache_updating:
                break
                
            self.log_to_cache_window(f"更新 {name} ({code}) 数据...")
            
            try:
                cache_path = os.path.join(self.cache_dir, f"index_{code}.csv")
                df = ak.stock_zh_index_daily(symbol=code)
                
                if df is None or df.empty:
                    self.log_to_cache_window(f"  警告: 未获取到 {name} 数据")
                    continue
                    
                # 保存数据
                df.to_csv(cache_path, index=False)
                self.log_to_cache_window(f"  已保存 {len(df)} 条记录")
                
                # 更新进度
                time.sleep(0.3)
                
            except Exception as e:
                self.log_to_cache_window(f"  更新失败: {str(e)}")


    def update_north_money_cache(self):
        """更新北向资金缓存数据"""
        self.log_to_cache_window("更新北向资金数据...")
        
        try:
            cache_path = os.path.join(self.cache_dir, "north_money.csv")
            
            # 尝试多种接口
            df = None
            for api_version in range(1, 4):
                if not self.cache_updating:
                    break
                    
                try:
                    if api_version == 1:
                        df = ak.stock_hsgt_north_net_flow_in_em()
                    elif api_version == 2:
                        df = ak.stock_individual_fund_flow_rank()
                    elif api_version == 3:
                        df = ak.stock_market_fund_flow()
                    
                    if df is not None and not df.empty:
                        self.log_to_cache_window(f"  使用接口 {api_version} 获取成功")
                        break
                except:
                    pass
            
            if df is None or df.empty:
                self.log_to_cache_window("  警告: 无法获取北向资金数据")
                return
                
            # 处理数据
            if 'date' in df.columns:
                df['date'] = pd.to_datetime(df['date'])
            elif '日期' in df.columns:
                df['date'] = pd.to_datetime(df['日期'])
            
            # 重命名列
            if 'net_amount' in df.columns:
                df = df.rename(columns={'net_amount': 'north_net_inflow'})
            elif '主力净流入-净额' in df.columns:
                df = df.rename(columns={'主力净流入-净额': 'north_net_inflow'})
            
            # 转换为亿元单位
            if 'north_net_inflow' in df.columns:
                df['north_net_inflow'] = df['north_net_inflow'] / 100000000
            
            # 保存数据
            df[['date', 'north_net_inflow']].to_csv(cache_path, index=False)
            self.log_to_cache_window(f"北向资金数据已保存，共 {len(df)} 条记录")
            
        except Exception as e:
            self.log_to_cache_window(f"北向资金更新失败: {str(e)}")

    def update_turnover_cache(self):
        """更新成交额缓存数据"""
        self.log_to_cache_window("更新成交额数据...")
        
        try:
            # 获取最近一年的数据
            end_date = datetime.now().date()
            start_date = end_date - relativedelta(years=1)
            
            self.log_to_cache_window(f"  获取 {start_date} 至 {end_date} 的数据...")
            
            # 获取交易日列表
            trade_days = self.trading_day_checker.get_trading_days(start_date, end_date)
            if not trade_days:
                self.log_to_cache_window("  警告: 无交易日数据")
                return
                
            # 准备数据容器
            sse_data = []
            szse_data = []
            dates_str = [d.strftime("%Y%m%d") for d in trade_days]
            
            total_days = len(dates_str)
            for i, date_str in enumerate(dates_str):
                if not self.cache_updating:
                    break
                    
                # 更新进度
                progress = (i / total_days) * 100
                self.root.after(0, lambda: self.cache_progress.configure(value=progress))
                
                # 获取上交所数据
                sse = self.data_fetcher.fetch_sse_turnover(date_str)
                # 获取深交所数据
                szse = self.data_fetcher.fetch_szse_turnover(date_str)
                
                sse_data.append(sse)
                szse_data.append(szse)
                
                # 每10天记录一次
                if i % 10 == 0 or i == total_days - 1:
                    self.log_to_cache_window(f"    已处理 {i+1}/{total_days} 天")
            
            if not self.cache_updating:
                return
                
            # 创建DataFrame
            df = pd.DataFrame({
                'date_str': dates_str,
                'sse_turnover': sse_data,
                'szse_turnover': szse_data
            })
            
            # 转换日期类型
            df['date'] = pd.to_datetime(df['date_str'], format='%Y%m%d')
            df = df.drop(columns=['date_str'])
            df['total_turnover'] = df['sse_turnover'] + df['szse_turnover']
            
            # 保存数据
            cache_path = os.path.join(self.cache_dir, "turnover_cache.csv")
            df.to_csv(cache_path, index=False)
            self.log_to_cache_window(f"成交额数据已保存，共 {len(df)} 条记录")
            
        except Exception as e:
            self.log_to_cache_window(f"成交额更新失败: {str(e)}")       

    def update_main_money_cache(self):
        """更新主力资金缓存数据"""
        self.log_to_cache_window("更新主力资金数据...")
        
        try:
            cache_path = os.path.join(self.cache_dir, "main_money_flow.csv")
            current_year = datetime.now().year
            start_year = current_year - 5  # 获取最近5年数据
            
            all_data = []
            
            for year in range(start_year, current_year + 1):
                if not self.cache_updating:
                    break
                    
                self.log_to_cache_window(f"  获取 {year} 年数据...")
                
                try:
                    df = ak.stock_market_fund_flow(year=str(year))
                    if df is None or df.empty:
                        self.log_to_cache_window(f"    警告: 未获取到 {year} 年数据")
                        continue
                        
                    # 处理数据
                    df['date'] = pd.to_datetime(df['日期'])
                    df['main_net_inflow'] = df['主力净流入-净额'] / 100000000  # 转换为亿元
                    
                    # 添加到总数据
                    all_data.append(df[['date', 'main_net_inflow']])
                    self.log_to_cache_window(f"    获取到 {len(df)} 条记录")
                    
                    time.sleep(1)  # 避免频繁请求
                    
                except Exception as e:
                    self.log_to_cache_window(f"    获取失败: {str(e)}")
            
            if all_data and self.cache_updating:
                # 合并所有数据
                combined = pd.concat(all_data)
                combined = combined.sort_values('date').drop_duplicates('date')
                
                # 保存数据
                combined.to_csv(cache_path, index=False)
                self.log_to_cache_window(f"主力资金数据已保存，共 {len(combined)} 条记录")
        
        except Exception as e:
            self.log_to_cache_window(f"主力资金更新失败: {str(e)}")

    def cancel_cache_update(self):
        """取消缓存更新"""
        if self.cache_updating:
            self.log_to_cache_window("用户取消了缓存更新")
            self.cache_updating = False
            
        if self.cache_update_window:
            self.cache_update_window.destroy()
            self.cache_update_window = None

    def _add_log_message(self, message):
        """添加日志消息（主线程安全）"""
        if not self.cache_log_text:
            return
            
        timestamp = datetime.now().strftime("%H:%M:%S")
        log_message = f"[{timestamp}] {message}\n"
        
        self.cache_log_text.configure(state=tk.NORMAL)
        self.cache_log_text.insert(tk.END, log_message)
        self.cache_log_text.configure(state=tk.DISABLED)
        self.cache_log_text.yview(tk.END)  # 滚动到底部

    def auto_update_cache_on_start(self):
        """程序启动时自动更新缓存（增量更新）"""
        try:
            self.logger.log("开始自动更新缓存数据...")
            
            # 在后台线程执行缓存更新
            threading.Thread(
                target=self.incremental_update_all_cache,
                daemon=True
            ).start()
        except Exception as e:
            self.logger.log(f"自动更新缓存失败: {str(e)}")
            self.status_var.set("缓存更新失败，但分析将继续")

    def incremental_update_all_cache(self):
        """增量更新所有缓存数据"""
        try:
            self.root.after(0, lambda: self.status_var.set("🔄 正在更新缓存数据..."))
            now = datetime.now().date()
            
            # 1. 更新指数数据
            index_codes = ["sh000001", "sz399001", "sz399006", "sh000688", "sh000300"]
            for code in index_codes:
                self.incremental_update_index_cache(code, now)
                time.sleep(0.5)  # 避免请求过于频繁
                
            # 2. 更新主力资金数据
            self.incremental_update_main_money_cache(now)
            
            # 3. 更新北向资金数据
            self.incremental_update_north_money_cache(now)
            
            # 4. 更新成交额数据
            self.incremental_update_turnover_cache(now)
            
            self.root.after(0, lambda: self.status_var.set("✅ 缓存更新完成！"))
            self.logger.log("所有缓存数据已增量更新")
            
            # 缓存更新完成后触发分析
            self.root.after(100, self.initial_load)
        except Exception as e:
            self.logger.log(f"增量更新失败: {str(e)}")
            self.root.after(0, lambda: self.status_var.set(f"缓存更新失败: {str(e)}"))

    def incremental_update_index_cache(self, index_code, end_date):
        """增量更新指数缓存数据"""
        cache_path = os.path.join(self.cache_dir, f"index_{index_code}.csv")
        
        # 获取现有缓存的最新日期
        cache_latest = None
        if os.path.exists(cache_path):
            try:
                cache_df = pd.read_csv(cache_path, parse_dates=['date'])
                if not cache_df.empty:
                    cache_latest = cache_df['date'].max().date()
            except:
                pass
        
        # 确定需要更新的日期范围
        start_date = cache_latest + timedelta(days=1) if cache_latest else date(1990, 1, 1)
        if start_date > end_date:
            self.logger.log(f"{index_code} 缓存已是最新，无需更新")
            return
            
        # 获取新数据
        self.logger.log(f"更新指数 {index_code} ({start_date} 至 {end_date})")
        new_data = ak.stock_zh_index_daily(symbol=index_code)
        if new_data is None or new_data.empty:
            raise Exception(f"获取指数 {index_code} 数据失败")
        
        # 过滤需要的数据
        new_data['date'] = pd.to_datetime(new_data['date'])
        mask = (new_data['date'] >= pd.Timestamp(start_date)) & (new_data['date'] <= pd.Timestamp(end_date))
        update_data = new_data.loc[mask]
        
        if update_data.empty:
            self.logger.log(f"指数 {index_code} 无新数据")
            return
            
        # 合并到缓存
        if os.path.exists(cache_path):
            full_data = pd.concat([cache_df, update_data])
            full_data = full_data.drop_duplicates('date').sort_values('date')
        else:
            full_data = update_data
            
        # 保存更新后的缓存
        full_data.to_csv(cache_path, index=False)
        self.logger.log(f"指数 {index_code} 缓存已更新，新增 {len(update_data)} 条记录")

    def incremental_update_main_money_cache(self, end_date):
        """增量更新主力资金缓存"""
        cache_path = os.path.join(self.cache_dir, "main_money_flow.csv")
        
        # 获取现有缓存的最新日期
        cache_latest = None
        if os.path.exists(cache_path):
            try:
                cache_df = pd.read_csv(cache_path, parse_dates=['date'])
                if not cache_df.empty:
                    cache_latest = cache_df['date'].max().date()
            except:
                pass
        
        # 确定需要更新的日期范围
        start_date = cache_latest + timedelta(days=1) if cache_latest else end_date - relativedelta(years=2)
        if start_date > end_date:
            self.logger.log("主力资金缓存已是最新，无需更新")
            return
            
        self.logger.log(f"更新主力资金数据 ({start_date} 至 {end_date})")
        
        # 分年获取数据
        all_data = []
        for year in range(start_date.year, end_date.year + 1):
            try:
                df = ak.stock_market_fund_flow(year=str(year))
                if not df.empty:
                    df['date'] = pd.to_datetime(df['日期'])
                    df['main_net_inflow'] = df['主力净流入-净额'] / 100000000  # 转换为亿元
                    all_data.append(df[['date', 'main_net_inflow']])
            except Exception as e:
                self.logger.log(f"主力资金 {year} 年数据获取失败: {str(e)}")
            time.sleep(1)
        
        if not all_data:
            self.logger.log("未获取到新的主力资金数据")
            return
            
        new_data = pd.concat(all_data)
        new_data = new_data.sort_values('date')
        
        # 过滤日期范围
        mask = (new_data['date'] >= pd.Timestamp(start_date)) & (new_data['date'] <= pd.Timestamp(end_date))
        update_data = new_data.loc[mask]
        
        if update_data.empty:
            self.logger.log("主力资金无新数据")
            return
            
        # 合并到缓存
        if os.path.exists(cache_path):
            full_data = pd.concat([cache_df, update_data])
        else:
            full_data = update_data
            
        # 去重并排序
        full_data = full_data.drop_duplicates('date').sort_values('date')
        full_data.to_csv(cache_path, index=False)
        self.logger.log(f"主力资金缓存已更新，新增 {len(update_data)} 条记录")

    def incremental_update_north_money_cache(self, end_date):
        """增量更新北向资金缓存"""
        cache_path = os.path.join(self.cache_dir, "north_money.csv")
        
        # 获取现有缓存的最新日期
        cache_latest = None
        if os.path.exists(cache_path):
            try:
                cache_df = pd.read_csv(cache_path, parse_dates=['date'])
                if not cache_df.empty:
                    cache_latest = cache_df['date'].max().date()
            except:
                pass
        
        # 确定需要更新的日期范围
        start_date = cache_latest + timedelta(days=1) if cache_latest else end_date - relativedelta(years=1)
        if start_date > end_date:
            self.logger.log("北向资金缓存已是最新，无需更新")
            return
            
        self.logger.log(f"更新北向资金数据 ({start_date} 至 {end_date})")
        
        # 获取新数据
        try:
            df = ak.stock_hsgt_north_net_flow_in_em()
            if df is None or df.empty:
                raise Exception("获取北向资金数据失败")
                
            df['date'] = pd.to_datetime(df['date'])
            mask = (df['date'] >= pd.Timestamp(start_date)) & (df['date'] <= pd.Timestamp(end_date))
            update_data = df.loc[mask]
            
            if update_data.empty:
                self.logger.log("北向资金无新数据")
                return
                
            # 合并到缓存
            if os.path.exists(cache_path):
                full_data = pd.concat([cache_df, update_data])
            else:
                full_data = update_data
                
            full_data = full_data.drop_duplicates('date').sort_values('date')
            full_data.to_csv(cache_path, index=False)
            self.logger.log(f"北向资金缓存已更新，新增 {len(update_data)} 条记录")
        except Exception as e:
            self.logger.log(f"北向资金更新失败: {str(e)}")

    def incremental_update_turnover_cache(self, end_date):
        """增量更新成交额缓存"""
        # 加载现有缓存
        cached_data = self.data_fetcher._load_turnover_cache()
        
        # 获取缓存的最新日期
        cache_latest = cached_data['date'].max().date() if not cached_data.empty else None
        
        # 确定需要更新的日期范围
        start_date = cache_latest + timedelta(days=1) if cache_latest else end_date - relativedelta(years=1)
        if start_date > end_date:
            self.logger.log("成交额缓存已是最新，无需更新")
            return
            
        self.logger.log(f"更新成交额数据 ({start_date} 至 {end_date})")
        
        # 获取新数据
        new_data = self.data_fetcher._fetch_full_turnover_data(start_date, end_date)
        
        if new_data.empty:
            self.logger.log("成交额无新数据")
            return
            
        # 合并数据
        combined = pd.concat([cached_data, new_data])
        combined = combined.drop_duplicates('date').sort_values('date')
        
        # 保存更新后的缓存
        self.data_fetcher._save_turnover_cache(combined)
        self.logger.log(f"成交额缓存已更新，新增 {len(new_data)} 条记录")

        self.root.after(100, self.auto_update_cache_on_start)


    def _clean_temp_data(self):
        """清理临时数据（新增方法）"""
        try:
            self.temp_data_cache.clear()
            self.logger.log("临时数据已清理")
        except Exception as e:
            self.logger.log(f"清理失败: {str(e)}")



    def schedule_turnover_update(self, *args):
        """成交额更新调度（防抖处理）"""
        if self.turnover_update_job:
            self.root.after_cancel(self.turnover_update_job)
        self.turnover_update_job = self.root.after(800, self.trigger_turnover_update)

    def trigger_turnover_update(self):
        """触发成交额更新"""
        try:
            self.logger.log("触发成交额更新检查")
            
            if not self.validate_dates(show_error=False):
                self.logger.log("日期验证失败，取消成交额更新")
                return
            
            current_params = (
                self.start_date_picker.get_date().strftime("%Y%m%d"),
                self.end_date_picker.get_date().strftime("%Y%m%d")
            )
            
            if hasattr(self, 'last_turnover_params') and current_params == self.last_turnover_params:
                self.logger.log("成交额参数未变化，取消更新")
                return
                
            self.last_turnover_params = current_params
            self.logger.log(f"开始成交额分析，参数：{current_params}")
            self.start_turnover_analysis()
            
        except Exception as e:
            self.logger.log(f"触发成交额更新失败: {str(e)}")
            self.root.after(0, lambda msg=str(e): self.status_var.set(f"成交额更新失败: {msg}"))


    def start_turnover_analysis(self):
        """启动成交额分析"""
        try:
            if self.turnover_analysis_running:
                self.logger.log("已有成交额分析在进行中，取消本次请求")
                return
                
            self.turnover_analysis_running = True
            start_date = self.start_date_picker.get_date()
            end_date = self.end_date_picker.get_date()
            print("start_turnover_analysis in")
            analysis_thread = threading.Thread(
                target=self.perform_turnover_analysis,
                args=(start_date.date(), end_date.date()),  # 确保传入date对象
                daemon=True
            )
            analysis_thread.start()
        except Exception as e:
            self.root.after(0, lambda msg=str(e): self.status_var.set(f"启动失败: {msg}"))

    def perform_turnover_analysis(self, start_date, end_date):
        """执行成交额分析（独立数据版本）"""
        try:
            self.logger.log(f"获取成交额数据 {start_date} 至 {end_date}")
            
            # 直接获取成交额数据
            turnover_data = self.data_fetcher.get_market_turnover(start_date, end_date)
            
            # 主线程更新
            self.root.after(0, lambda: self._update_turnover_ui(turnover_data))
            
        except Exception as e:
            error_msg = f"成交额分析失败: {str(e)}"
            self.root.after(0, lambda msg=error_msg: self.status_var.set(msg))
            self.logger.log(error_msg)
        finally:
            self.turnover_analysis_running = False

    def _update_turnover_ui(self, turnover_data):
        """更新成交额界面（独立数据版本）"""
        try:
            print("_update_turnover_ui 111")
            if turnover_data is None or turnover_data.empty:
                self.logger.log("成交额数据为空，跳过更新")
                self.status_var.set("警告：未获取到成交额数据")
                return

            # 直接使用成交额数据绘图
            self.plot_turnover_trend(turnover_data)
            print("_update_turnover_ui in")
            # 更新统计信息
            self._update_turnover_stats(turnover_data)
            
            self.status_var.set(f"成交额已更新（{len(turnover_data)}个交易日）")
            
        except Exception as e:
            self.logger.log(f"成交额界面更新失败: {str(e)}")
            self.root.after(0, lambda msg=str(e): self.status_var.set(f"错误: {msg}"))

    def _update_turnover_stats(self, data):
        """更新成交额统计信息（健壮版）"""
        try:
            print("成交额统计进入")
            if not hasattr(self, 'max_turnover'):
                raise AttributeError("统计变量未初始化")
                
            valid_data = data[data['total_turnover'].notna()]
            
            if not valid_data.empty:
                stats = {
                    'max': valid_data['total_turnover'].max(),
                    'min': valid_data['total_turnover'].min(),
                    'mean': valid_data['total_turnover'].mean(),
                    'total': valid_data['total_turnover'].sum()
                }
                
                # 使用条件判断确保变量存在
                if hasattr(self, 'max_turnover'):
                    self.max_turnover.set(f"最大成交额: {stats['max']:.2f} 亿元")
                    print("set max_turnover")
                if hasattr(self, 'min_turnover'):    
                    self.min_turnover.set(f"最小成交额: {stats['min']:.2f} 亿元")
                    print("set min_turnover")
                if hasattr(self, 'avg_turnover'):
                    self.avg_turnover.set(f"平均成交额: {stats['mean']:.2f} 亿元")
                    print("set avg_turnover")
                if hasattr(self, 'total_turnover'):
                    self.total_turnover.set(f"总成交额: {stats['total']/10000:.2f} 万亿元")
                    print("set total_turnover")
                    
            else:
                self.max_turnover.set("--")
                self.min_turnover.set("--")
                self.avg_turnover.set("--")
                self.total_turnover.set("--")
                
        except Exception as e:
            self.logger.log(f"统计更新失败: {str(e)}")

    def schedule_fund_update(self, *args):
        """资金流向更新调度（防抖处理）"""
        if self.fund_update_job:
            self.root.after_cancel(self.fund_update_job)
        self.fund_update_job = self.root.after(800, self.trigger_fund_update)

    def trigger_fund_update(self):
        """触发资金流向更新"""
        try:
            # 添加调试日志
            self.logger.log("触发资金流向更新检查")
            
            if not self.validate_dates(show_error=False):
                self.logger.log("日期验证失败，取消更新")
                return
            
            current_params = (
                self.start_date_picker.get_date().strftime("%Y%m%d"),
                self.end_date_picker.get_date().strftime("%Y%m%d"),
                self.north_enabled.get()
            )
            
            # 检查参数是否有变化
            if hasattr(self, 'last_fund_params') and current_params == self.last_fund_params:
                self.logger.log("资金流向参数未变化，取消更新")
                return
                
            self.last_fund_params = current_params
            self.logger.log(f"开始资金流向分析，参数：{current_params}")
            self.start_fund_analysis()
            
        except Exception as e:
            self.logger.log(f"触发资金流向更新失败: {str(e)}")
            messagebox.showerror("更新错误", f"资金流向更新失败: {str(e)}")


    def start_fund_analysis(self):
        """启动资金流向分析"""
        try:
            print("start_fund_analysis in1")
            if self.fund_analysis_running:
                self.logger.log("已有资金分析在进行中，取消本次请求")
                return
            print("start_fund_analysis in1")    
            self.fund_analysis_running = True
            start_date = self.start_date_picker.get_date()
            end_date = self.end_date_picker.get_date()
            print("start_fund_analysis in")
            analysis_thread = threading.Thread(
                target=self.perform_fund_analysis,
                args=(start_date, end_date),
                daemon=True
            )
            analysis_thread.start()
        except Exception as e:
            # 修复点：通过默认参数捕获当前异常值
            self.root.after(0, lambda e=e: self.status_var.set(f"启动失败: {str(e)}"))

    def perform_fund_analysis(self, start_date, end_date):
        """执行资金分析（带异常处理）"""
        try:
            # 在后台线程获取数据
            main_data = self.data_fetcher.get_main_money_flow_data(start_date, end_date)
            north_data = self.data_fetcher.get_north_flow_data(start_date, end_date) if self.north_enabled.get() else None
            print("perform_fund_analysis in")
            # 回到主线程更新
            self.root.after(0, lambda: self._update_fund_ui(main_data, north_data))
            
        except Exception as e:
            error_msg = f"资金分析失败: {str(e)}"
            #self.root.after(0, lambda: self.status_var.set(error_msg))
            self.root.after(0, lambda msg=error_msg: self.status_var.set(msg))
            self.logger.log(error_msg)
        finally:
            self.fund_analysis_running = False

    
    def _update_fund_ui(self, main_data, north_data):
        """主线程更新UI"""
        print("_update_fund_ui in")
        try:
            # 合并数据
            merged = self.merge_data(
                self.current_data,  # 使用已有的指数数据
                north_data,
                main_data,
                self.north_enabled.get(),
                main_data is not None,
                self.turnover_data
            )
            
            # 更新图表
            self.plot_fund_flow_trend(merged, self.north_enabled.get(), True)
            self.plot_correlation_scatter(merged, self.north_enabled.get(), True)
            
            # 更新统计数据
            correlations = self.calculate_correlations(merged, self.north_enabled.get(), True)
            self.update_overview_cards(correlations, self.north_enabled.get(), True)
            
            self.status_var.set(f"资金流向已更新（{len(merged)}个交易日）")
        except Exception as e:
            messagebox.showerror("更新错误", f"界面更新失败: {str(e)}")

    def analyze_delivery_impact(self):
        """分析交割日对指数的影响（移除成交额分析）"""
        try:
            # 数据验证 - 确保基础数据存在
            if self.current_data is None:
                self.logger.log("交割日分析失败: current_data 为 None")
                messagebox.showinfo("提示", "请先完成基础分析")
                return
                
            if self.current_data.empty:
                self.logger.log(f"交割日分析失败: current_data 为空 (shape: {self.current_data.shape})")
                messagebox.showinfo("提示", "当前数据为空，请先完成基础分析")
                return
            
            # 关键列验证 - 确保包含必要列
            required_cols = {'date', 'pct_change'}  # 移除了'total_turnover'
            available_cols = set(self.current_data.columns)
            missing_cols = required_cols - available_cols
            
            if missing_cols:
                self.logger.log(f"交割日分析失败: 缺失必要列 {missing_cols} (可用列: {available_cols})")
                messagebox.showerror("数据缺失", f"缺失必要列: {', '.join(missing_cols)}")
                return

            # 统一日期类型为date
            try:
                self.current_data['trade_date'] = pd.to_datetime(
                    self.current_data['date']
                ).dt.date
            except Exception as e:
                self.logger.log(f"日期转换失败: {str(e)}")
                messagebox.showerror("日期错误", f"日期转换失败: {str(e)}")
                return

            # 获取分析参数
            days_before = 3
            days_after = 3
            
            # 获取交割日数据（确保返回date类型）
            start_date = self.current_data['trade_date'].min()
            end_date = self.current_data['trade_date'].max()
            
            self.logger.log(f"交割日分析范围: {start_date} 至 {end_date}")
            
            # 检查数据范围是否足够
            if (end_date - start_date).days < 30:  # 至少需要30天的数据
                self.logger.log(f"数据范围不足: 仅 {(end_date - start_date).days} 天")
                messagebox.showinfo("数据不足", "当前数据范围太小，无法进行交割日分析（至少需要30天数据）")
                return
                
            delivery_dates = self.delivery_calculator.get_monthly_delivery_dates(
                start_date, end_date
            )
            
            # 检查是否有交割日
            if not delivery_dates:
                self.logger.log("当前数据范围内无交割日")
                messagebox.showinfo("提示", "当前数据范围内无交割日")
                return
            
            self.logger.log(f"找到{len(delivery_dates)}个交割日: {delivery_dates}")
            
            # 准备分析数据
            analysis_data = []
            valid_delivery_count = 0
            skipped_dates = []
            
            for delivery_date in delivery_dates:
                # 计算窗口期（全部使用date类型）
                start_window = delivery_date - timedelta(days=days_before)
                end_window = delivery_date + timedelta(days=days_after)
                
                # 筛选数据（使用date类型比较）
                window_data = self.current_data[
                    (self.current_data['trade_date'] >= start_window) &
                    (self.current_data['trade_date'] <= end_window)
                ].copy()
                
                if window_data.empty:
                    skipped_dates.append(delivery_date)
                    self.logger.log(f"警告：交割日{delivery_date}附近无数据")
                    continue
                    
                # 计算相对交割日的天数
                window_data['day_offset'] = (
                    window_data['trade_date'] - delivery_date
                ).apply(lambda x: x.days)
                
                # 过滤有效偏移范围（-3到+3天）
                window_data = window_data[
                    (window_data['day_offset'] >= -days_before) & 
                    (window_data['day_offset'] <= days_after)
                ]
                
                # 确保有足够数据点（至少3天）
                if len(window_data) >= 3:
                    analysis_data.append(window_data)
                    valid_delivery_count += 1
                    self.logger.log(f"交割日{delivery_date}找到{len(window_data)}条数据")
                else:
                    skipped_dates.append(delivery_date)
                    self.logger.log(f"警告：交割日{delivery_date}附近数据不足（仅{len(window_data)}条）")

            # 检查是否有有效数据
            if not analysis_data:
                skip_msg = f"跳过的交割日: {skipped_dates}" if skipped_dates else "无跳过的交割日"
                self.logger.log(f"交割日附近无有效数据，请扩大分析范围. {skip_msg}")
                messagebox.showinfo("提示", "交割日附近无有效数据，请扩大分析范围")
                return
                
            # 合并分析数据
            combined = pd.concat(analysis_data)
            
            # 分组计算统计量 - 只关注涨跌幅
            grouped = combined.groupby('day_offset')['pct_change'].agg(
                mean_change='mean',
                std_change='std',
                count='count'
            ).reset_index()
            
            # 检查是否有足够数据点
            if grouped.empty:
                self.logger.log("分组后数据为空")
                messagebox.showinfo("提示", "数据点不足，无法进行统计分析")
                return
                
            if len(grouped) < 3:
                self.logger.log(f"数据点不足，仅{len(grouped)}个分组")
                messagebox.showinfo("提示", f"数据点不足（仅{len(grouped)}个），无法进行统计分析")
                return
                
            # 准备结果 - 不再包含成交额数据
            result = {
                'grouped': grouped,
                'delivery_dates': delivery_dates,
                'valid_delivery_count': valid_delivery_count,
                'skipped_dates': skipped_dates,
                'raw_data': combined  # 添加原始数据字段
            }
            
            # 更新图表
            self.plot_delivery_impact(result)
            
            # 显示统计信息
            self.show_delivery_stats(result)

        except Exception as e:
            error_msg = f"交割日分析失败: {str(e)}"
            self.logger.log(error_msg)
            self.logger.log(traceback.format_exc())
            messagebox.showerror("分析错误", error_msg)

    def plot_delivery_impact(self, result):
        """绘制交割日影响图表（带涨跌柱状图）"""
        self.delivery_ax1.clear()
        self.delivery_ax2.clear()
        
        # 检查数据是否有效
        if 'raw_data' not in result or result['raw_data'].empty:
            # 显示友好的提示信息
            self.delivery_ax1.text(
                0.5, 0.5, 
                "无足够数据展示图表\n请先完成基础分析并确保数据有效", 
                ha='center', va='center', 
                fontsize=12, 
                color='gray'
            )
            self.delivery_ax1.set_title("交割日影响分析 - 数据不足")
            self.delivery_ax1.set_axis_off()
            self.delivery_ax2.set_axis_off()
            self.delivery_fig.tight_layout()
            self.delivery_canvas.draw()
            return
        
        # 准备数据
        raw_data = result['raw_data']
        
        # 第一张图：涨跌幅分布
        ax1 = self.delivery_ax1
        ax1.set_title("交割日前后涨跌幅分布", fontsize=14)
        
        # 绘制涨跌柱状图（红色上涨，绿色下跌）
        for offset in sorted(raw_data['day_offset'].unique()):
            offset_data = raw_data[raw_data['day_offset'] == offset]
            
            # 计算涨跌天数
            positive_days = offset_data[offset_data['pct_change'] > 0]
            negative_days = offset_data[offset_data['pct_change'] < 0]
            
            # 绘制上涨天数柱状图
            ax1.bar(
                offset - 0.2, 
                len(positive_days),
                width=0.4,
                color='red',
                alpha=0.7,
                label='上涨' if offset == min(raw_data['day_offset']) else ""
            )
            
            # 绘制下跌天数柱状图
            ax1.bar(
                offset + 0.2, 
                len(negative_days),
                width=0.4,
                color='green',
                alpha=0.7,
                label='下跌' if offset == min(raw_data['day_offset']) else ""
            )
            
            # 添加数量标签
            if len(positive_days) > 0:
                ax1.text(
                    offset - 0.2, 
                    len(positive_days) + 0.5, 
                    f"{len(positive_days)}",
                    ha='center',
                    color='red'
                )
                
            if len(negative_days) > 0:
                ax1.text(
                    offset + 0.2, 
                    len(negative_days) + 0.5, 
                    f"{len(negative_days)}",
                    ha='center',
                    color='green'
                )
        
        # 添加交割日参考线
        ax1.axvline(0, color='blue', linestyle='--', alpha=0.7, label='交割日')
        
        # 设置图表属性
        ax1.set_xlabel("相对交割日天数", fontsize=12)
        ax1.set_ylabel("涨跌天数", fontsize=12)
        ax1.grid(True, linestyle='--', alpha=0.7)
        ax1.legend(loc='best')
        
        # 第二张图：平均涨跌幅变化
        ax2 = self.delivery_ax2
        ax2.set_title("交割日前后平均涨跌幅变化", fontsize=14)
        
        # 绘制平均涨跌幅变化线
        ax2.plot(
            result['grouped']['day_offset'],
            result['grouped']['mean_change'],
            color='blue',
            marker='o',
            label='平均涨跌幅'
        )
        
        # 添加误差线
        ax2.fill_between(
            result['grouped']['day_offset'],
            result['grouped']['mean_change'] - result['grouped']['std_change'],
            result['grouped']['mean_change'] + result['grouped']['std_change'],
            color='blue',
            alpha=0.2,
            label='波动范围'
        )
        
        # 添加交割日参考线
        ax2.axvline(0, color='red', linestyle='--', alpha=0.7)
        
        # 设置图表属性
        ax2.set_xlabel("相对交割日天数", fontsize=12)
        ax2.set_ylabel("涨跌幅 (%)", fontsize=12)
        ax2.grid(True, linestyle='--', alpha=0.7)
        ax2.legend(loc='best')
        
        # 自动调整布局
        self.delivery_fig.tight_layout()
        self.delivery_canvas.draw()

    def show_delivery_stats(self, result):
        """显示交割日统计信息（完整版）"""
        stats = []
        
        # 基本统计信息
        stats.append(f"分析周期：{result['grouped']['day_offset'].min()} 至 {result['grouped']['day_offset'].max()} 天")
        stats.append(f"包含交割日数量：{len(result['delivery_dates'])}个")
        stats.append(f"有效交割日数量：{result['valid_delivery_count']}个")
        
        # 计算关键指标
        delivery_day = result['grouped'][result['grouped']['day_offset'] == 0]
        if not delivery_day.empty:
            # 获取原始数据计算涨跌概率
            if 'raw_data' in result and not result['raw_data'].empty:
                delivery_raw = result['raw_data'][result['raw_data']['day_offset'] == 0]
                
                # 计算涨跌概率
                positive_days = delivery_raw[delivery_raw['pct_change'] > 0]
                negative_days = delivery_raw[delivery_raw['pct_change'] < 0]
                neutral_days = delivery_raw[delivery_raw['pct_change'] == 0]
                
                total_days = len(delivery_raw)
                positive_prob = len(positive_days) / total_days * 100 if total_days > 0 else 0
                negative_prob = len(negative_days) / total_days * 100 if total_days > 0 else 0
                
                # 添加统计信息
                stats.append(f"交割日平均涨跌幅: {delivery_day['mean_change'].values[0]:.2f}%")
                stats.append(f"上涨概率: {positive_prob:.1f}% ({len(positive_days)}/{total_days})")
                stats.append(f"下跌概率: {negative_prob:.1f}% ({len(negative_days)}/{total_days})")
                stats.append(f"波动率: {delivery_day['std_change'].values[0]:.2f}%")
            else:
                stats.append("交割日原始数据不可用")
            
            # 计算涨跌概率
            positive_days = delivery_raw[delivery_raw['pct_change'] > 0]
            negative_days = delivery_raw[delivery_raw['pct_change'] < 0]
            neutral_days = delivery_raw[delivery_raw['pct_change'] == 0]
            
            total_days = len(delivery_raw)
            positive_prob = len(positive_days) / total_days * 100 if total_days > 0 else 0
            negative_prob = len(negative_days) / total_days * 100 if total_days > 0 else 0
            
            # 添加统计信息
            stats.append(f"交割日平均涨跌幅: {delivery_day['mean_change'].values[0]:.2f}%")
            stats.append(f"上涨概率: {positive_prob:.1f}% ({len(positive_days)}/{total_days})")
            stats.append(f"下跌概率: {negative_prob:.1f}% ({len(negative_days)}/{total_days})")
            stats.append(f"波动率: {delivery_day['std_change'].values[0]:.2f}%")
        
        # 计算交割日前后的表现
        before_days = result['grouped'][result['grouped']['day_offset'] < 0]
        after_days = result['grouped'][result['grouped']['day_offset'] > 0]
        
        if not before_days.empty:
            stats.append(f"交割日前平均涨跌幅: {before_days['mean_change'].mean():.2f}%")
        if not after_days.empty:
            stats.append(f"交割日后平均涨跌幅: {after_days['mean_change'].mean():.2f}%")
        
        # 计算交割日效应强度
        if not delivery_day.empty and not before_days.empty:
            effect_strength = delivery_day['mean_change'].values[0] - before_days['mean_change'].mean()
            stats.append(f"交割日效应强度: {effect_strength:.2f}%")
                    
        # 在交割日选项卡中添加统计信息显示
        for widget in self.delivery_frame.winfo_children():
            if isinstance(widget, ttk.Label):
                widget.destroy()
        
        # 创建带颜色的统计标签
        stats_frame = ttk.Frame(self.delivery_frame)
        stats_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
        
        for i, stat in enumerate(stats):
            # 为关键统计添加颜色
            if "上涨概率" in stat:
                color = "red"
            elif "下跌概率" in stat:
                color = "green"
            elif "平均涨跌幅" in stat:
                # 提取数值判断正负
                value_str = stat.split(":")[1].split("%")[0].strip()
                try:
                    value = float(value_str)
                    color = "red" if value > 0 else "green"
                except:
                    color = "black"
            elif "效应强度" in stat:
                # 提取数值判断正负
                value_str = stat.split(":")[1].split("%")[0].strip()
                try:
                    value = float(value_str)
                    color = "red" if value > 0 else "green"
                except:
                    color = "black"
            else:
                color = "black"
                
            label = ttk.Label(stats_frame, text=stat, font=("SimHei", 10), foreground=color)
            label.grid(row=i, column=0, sticky=tk.W, padx=5, pady=2)
    
    def check_akshare_version(self):
        """检查akshare版本并显示提示"""
        try:
            version = ak.__version__
            self.logger.log(f"已加载 akshare v{version}")
            # 检查是否为较新版本
            major, minor, _ = map(int, re.findall(r'\d+', version)[:3])
            if major < 1 or (major == 1 and minor < 30):
                messagebox.showinfo("版本提示", 
                    f"您的akshare版本为{version}，建议升级到最新版本以获取最佳兼容性。\n"
                    "可通过命令: 'pip install akshare --upgrade' 进行升级。")
        except Exception as e:
            self.logger.log(f"检查akshare版本时出错: {e}")
            messagebox.showerror("版本检查失败", f"无法确定akshare版本，请确保已安装最新版本")
    
    def create_widgets(self):
        """完整界面创建代码"""
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 配置网格布局
        main_frame.grid_rowconfigure(1, weight=1)  # 允许notebook扩展
        main_frame.grid_columnconfigure(0, weight=1)
        main_frame.grid_columnconfigure(1, weight=1)
        
        # 概览卡片（左栏）
        self.overview_frame = ttk.LabelFrame(main_frame, text="数据分析概览", padding="10")
        self.overview_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")
        
        # 控制面板（右栏）
        self.control_frame = ttk.LabelFrame(main_frame, text="分析参数设置", padding="10")
        self.control_frame.grid(row=0, column=1, padx=5, pady=5, sticky="nsew")
        
        # === 控制面板内容 ===
        # 指数选择
        ttk.Label(self.control_frame, text="选择指数:").grid(row=0, column=0, sticky=tk.W)
        self.index_var = tk.StringVar(value="sh000001")
        index_frame = ttk.Frame(self.control_frame)
        index_frame.grid(row=0, column=1, sticky=tk.W)
        
        index_options = [
            ("上证指数", "sh000001"),
            ("深证成指", "sz399001"),
            ("创业板指", "sz399006"),
            ("科创50", "sh000688"),
            ("沪深300", "sh000300")
        ]
        
        # 动态生成单选按钮
        for i, (text, value) in enumerate(index_options):
            row = i // 3
            col = i % 3
            ttk.Radiobutton(index_frame, text=text, variable=self.index_var, 
                        value=value).grid(row=row, column=col, padx=10, pady=5)
        
        # 日期选择
        end_date_default = datetime.now()
        start_date_default = end_date_default - relativedelta(months=6)
        
        ttk.Label(self.control_frame, text="开始日期:").grid(row=1, column=0, sticky=tk.W)
        self.start_date_picker = DatePicker(self.control_frame, start_date_default)
        self.start_date_picker.grid(row=1, column=1, sticky=tk.W)
        
        ttk.Label(self.control_frame, text="结束日期:").grid(row=2, column=0, sticky=tk.W)
        self.end_date_picker = DatePicker(self.control_frame, end_date_default)
        self.end_date_picker.grid(row=2, column=1, sticky=tk.W)
        
        # 资金类型选择
        ttk.Label(self.control_frame, text="分析选项:").grid(row=3, column=0, sticky=tk.W)
        option_frame = ttk.Frame(self.control_frame)
        option_frame.grid(row=3, column=1, sticky=tk.W)
        ttk.Checkbutton(option_frame, text="包含北向资金", variable=self.north_enabled).pack(side=tk.LEFT)
        
        # 状态提示
        ttk.Label(self.control_frame, text="※ 修改参数自动刷新", 
                foreground="#666", font=("SimHei", 9)).grid(
                    row=0, column=2, rowspan=4, 
                    padx=20, pady=5, sticky=tk.NSEW)
        
        # === 结果区域 ===
        self.notebook = ttk.Notebook(main_frame)
        self.notebook.grid(row=1, column=0, columnspan=2, sticky="nsew", pady=10)
        
        # 各选项卡初始化
        self.init_index_tab()        # 指数趋势
        self.init_fund_flow_tab()    # 资金流向趋势
        self.init_turnover_tab()     # 成交额趋势
        self.init_scatter_tab()      # 相关性分析
        self.init_data_tab()         # 详细数据
        self.init_delivery_tab()     # 交割日影响
        
        # === 状态栏 ===
        self.status_var = tk.StringVar(value="就绪")
        ttk.Label(main_frame, textvariable=self.status_var, 
                relief=tk.SUNKEN, anchor=tk.W).grid(row=2, column=0, columnspan=2, sticky="ew")
        
        # === 概览卡片内容 ===
        self.create_overview_cards()
        
    def init_index_tab(self):
        """初始化指数趋势选项卡（修复工具栏不显示问题）"""
        tab_frame = ttk.Frame(self.notebook)
        self.notebook.add(tab_frame, text="指数趋势")
        
        # === 创建绘图容器（关键：单独容器管理图表和工具栏）===
        plot_container = ttk.Frame(tab_frame)
        plot_container.pack(fill=tk.BOTH, expand=True)
        
        # 创建Matplotlib Figure
        self.index_fig = Figure(figsize=(10, 5), dpi=100)
        self.index_ax = self.index_fig.add_subplot(111)
        
        # 创建Canvas并嵌入到容器
        self.index_canvas = FigureCanvasTkAgg(self.index_fig, master=plot_container)
        
        # === 关键步骤1：先放置工具栏 ===
        # 创建导航工具栏（父容器必须与canvas一致）
        toolbar = NavigationToolbar2Tk(self.index_canvas, plot_container)
        toolbar.update()  # 生成工具栏按钮
        
        # === 关键步骤2：使用grid布局确保工具栏正确显示 ===
        # 工具栏使用grid布局在第一行
        toolbar.grid(row=0, column=0, sticky="ew")
        
        # Canvas放置在第二行
        self.index_canvas.get_tk_widget().grid(row=1, column=0, sticky="nsew")
        
        # === 配置网格权重保证扩展 ===
        plot_container.grid_rowconfigure(1, weight=1)  # Canvas行可扩展
        plot_container.grid_columnconfigure(0, weight=1)  # 单列扩展
        
        # === 添加自定义控制按钮 ===
        control_frame = ttk.Frame(tab_frame)
        control_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(control_frame, text="重置视图", 
                command=self._reset_index_view).pack(side=tk.LEFT, padx=5)
        
        ttk.Button(control_frame, text="保存图片",
                command=lambda: self._save_figure(self.index_fig)).pack(side=tk.LEFT)

    def _reset_index_view(self):
        """重置图表视图"""
        self.index_ax.autoscale()
        self.index_canvas.draw_idle()  # 更流畅的重绘方式

    def _save_figure(self, figure):
        """保存图表到文件"""
        from tkinter import filedialog
        filetypes = [
            ('PNG 图片', '*.png'),
            ('JPEG 图片', '*.jpg'),
            ('PDF 文档', '*.pdf'),
            ('SVG 矢量图', '*.svg')
        ]
        path = filedialog.asksaveasfilename(
            defaultextension=".png",
            filetypes=filetypes
        )
        if path:
            try:
                figure.savefig(path, dpi=300, bbox_inches='tight')
                self.logger.log(f"图表已保存至：{path}")
                messagebox.showinfo("保存成功", f"文件已保存至：\n{path}")
            except Exception as e:
                messagebox.showerror("保存失败", f"错误信息：\n{str(e)}")

    def init_turnover_tab(self):
        """初始化成交额趋势选项卡"""
        self.turnover_trend_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.turnover_trend_frame, text="成交额趋势")
        
        # 创建成交额趋势图
        self.turnover_fig = Figure(figsize=(10, 5), dpi=100)
        self.turnover_ax = self.turnover_fig.add_subplot(111)
        self.turnover_canvas = FigureCanvasTkAgg(self.turnover_fig, master=self.turnover_trend_frame)
        self.turnover_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, pady=10)

    def init_fund_flow_tab(self):
        """初始化资金流向趋势选项卡（修改后）"""
        self.fund_flow_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.fund_flow_frame, text="资金流向趋势")
        
        # 添加控制按钮
        control_frame = ttk.Frame(self.fund_flow_frame)
        control_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(control_frame, text="立即刷新", 
                command=self.trigger_fund_update).pack(side=tk.RIGHT, padx=5)
        
        # 创建图表
        self.fund_flow_fig = Figure(figsize=(10, 5), dpi=100)
        self.fund_flow_ax = self.fund_flow_fig.add_subplot(111)
        self.fund_flow_canvas = FigureCanvasTkAgg(self.fund_flow_fig, master=self.fund_flow_frame)
        self.fund_flow_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        
        # 添加工具栏
        toolbar = NavigationToolbar2Tk(self.fund_flow_canvas, self.fund_flow_frame)
        toolbar.update()

    def init_scatter_tab(self):
        """初始化相关性分析选项卡"""
        self.scatter_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.scatter_frame, text="相关性分析")
        
        # 创建散点图
        self.scatter_fig = Figure(figsize=(10, 5), dpi=100)
        self.scatter_ax = self.scatter_fig.add_subplot(111)
        self.scatter_canvas = FigureCanvasTkAgg(self.scatter_fig, master=self.scatter_frame)
        self.scatter_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def init_data_tab(self):
        """初始化详细数据选项卡"""
        self.data_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.data_frame, text="详细数据")
        
        # 创建数据表格
        self.data_tree = ttk.Treeview(self.data_frame)
        scrollbar = ttk.Scrollbar(self.data_frame, orient=tk.VERTICAL, command=self.data_tree.yview)
        self.data_tree.configure(yscroll=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.data_tree.pack(fill=tk.BOTH, expand=True)
        
        # 导出按钮
        self.export_btn = ttk.Button(self.data_frame, text="导出数据", command=self.export_data)
        self.export_btn.pack(side=tk.RIGHT, padx=10, pady=10)

    def init_delivery_tab(self):
        """初始化交割日影响选项卡"""
        self.delivery_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.delivery_frame, text="交割日影响")
        
        # 创建交割日分析图表
        self.delivery_fig = Figure(figsize=(10, 6), dpi=100)
        self.delivery_ax1 = self.delivery_fig.add_subplot(211)
        self.delivery_ax2 = self.delivery_fig.add_subplot(212)
        self.delivery_canvas = FigureCanvasTkAgg(self.delivery_fig, master=self.delivery_frame)
        self.delivery_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        ttk.Button(self.delivery_frame, text="分析交割日影响", command=self.analyze_delivery_impact).pack(pady=5)

    def setup_autoupdate(self):
        """设置自动更新绑定（添加交割日分析绑定）"""
        # 原有绑定
        self.index_var.trace_add('write', self.schedule_index_update)
        self.start_date_picker.date_var.trace_add('write', self.schedule_index_update)
        self.end_date_picker.date_var.trace_add('write', self.schedule_index_update)

        # 资金流向相关
        self.start_date_picker.date_var.trace_add('write', self.schedule_fund_update)
        self.end_date_picker.date_var.trace_add('write', self.schedule_fund_update)
        self.north_enabled.trace_add('write', self.schedule_fund_update)

        # 成交额绑定
        self.start_date_picker.date_var.trace_add('write', self.schedule_turnover_update)
        self.end_date_picker.date_var.trace_add('write', self.schedule_turnover_update)
        self.index_var.trace_add('write', self.schedule_turnover_update)
        
        # 新增：交割日分析绑定
        self.start_date_picker.date_var.trace_add('write', self.schedule_delivery_update)
        self.end_date_picker.date_var.trace_add('write', self.schedule_delivery_update)
        self.index_var.trace_add('write', self.schedule_delivery_update)

    def schedule_delivery_update(self, *args):
        """调度交割日更新（防抖处理）"""
        if self.delivery_update_job:
            self.root.after_cancel(self.delivery_update_job)
        self.delivery_update_job = self.root.after(1000, self.trigger_delivery_update)

    def trigger_delivery_update(self):
        """触发交割日更新"""
        try:
            self.logger.log("触发交割日更新检查")
            
            # 检查基础数据是否存在
            if self.current_data is None or self.current_data.empty:
                self.logger.log("基础数据未加载，跳过交割日更新")
                return
                
            # 检查日期有效性
            if not self.validate_dates(show_error=False):
                self.logger.log("日期验证失败，跳过交割日更新")
                return
                
            # 检查参数是否有变化
            current_params = (
                self.index_var.get(),
                self.start_date_picker.get_date().strftime("%Y%m%d"),
                self.end_date_picker.get_date().strftime("%Y%m%d")
            )
            
            if hasattr(self, 'last_delivery_params') and current_params == self.last_delivery_params:
                self.logger.log("交割日参数未变化，跳过更新")
                return
                
            self.last_delivery_params = current_params
            self.logger.log(f"开始交割日分析，参数：{current_params}")
            
            # 启动交割日分析
            self.analyze_delivery_impact()
            
        except Exception as e:
            self.logger.log(f"触发交割日更新失败: {str(e)}")

    def schedule_index_update(self, *args):
        """指数更新调度（新增方法）"""
        if self.update_job:
            self.root.after_cancel(self.update_job)
        self.update_job = self.root.after(800, self.trigger_index_update)

    def trigger_index_update(self):
        """触发指数更新（新增方法）"""
        if not self.validate_dates(show_error=False) or self.index_update_lock:
            return

        current_params = (
            self.index_var.get(),
            self.start_date_picker.get_date().strftime("%Y%m%d"),
            self.end_date_picker.get_date().strftime("%Y%m%d")
        )
        
        if current_params == self.last_index_params:
            return
            
        self.last_index_params = current_params
        self.start_index_analysis()

    def start_index_analysis(self):
        """启动指数分析（新增方法）"""
        try:
            start_date = self.start_date_picker.get_date()
            end_date = self.end_date_picker.get_date()
            
            analysis_thread = threading.Thread(
                target=self.perform_index_analysis,
                args=(start_date, end_date),
                daemon=True
            )
            analysis_thread.start()
        except Exception as e:
            self.status_var.set(f"指数更新失败: {str(e)}")

    def perform_index_analysis(self, start_date, end_date):
        """执行指数分析（新增方法）"""
        try:
            self.root.after(0, lambda: self.status_var.set("正在加载指数数据..."))
            
            # 获取指数数据
            index_code = self.index_var.get()
            index_data = self.data_fetcher.get_market_index_data(index_code, start_date, end_date)
            
            if index_data is None or index_data.empty:
                raise Exception("获取指数数据失败")

            # 更新图表和数据
            self.root.after(0, lambda: self.update_index_display(index_data))
            
        except Exception as e:
            self.root.after(0, lambda: self.status_var.set(f"指数分析失败: {str(e)}"))
            self.logger.log(f"指数分析错误: {traceback.format_exc()}")

    def update_index_display(self, index_data):
        """更新指数显示（带数据状态跟踪）"""
        try:
            # 更新指数趋势图
            self.plot_index_trend(index_data, self.index_var.get())
            
            # 更新数据表格（仅显示指数数据）
            self.current_data = index_data[['date', 'close', 'pct_change']]
            
            # 更新数据状态
            self.data_initialized = True
            self.data_source = "update_index_display"
            self.data_last_update = datetime.now()
            self.logger.log(f"✅ 指数显示更新完成，current_data已设置 (来源: {self.data_source}, 大小: {len(index_data)})")
            
            # 清除其他分析的缓存
            self.turnover_cache.clear()
            self.delivery_analysis_cache.clear()
            self.logger.log("🆑 已清除成交额和交割日分析缓存")
            
            # 更新状态
            self.status_var.set(f"指数数据更新完成（{len(index_data)}个交易日）")
            
        except Exception as e:
            self.logger.log(f"指数显示更新失败: {str(e)}")
            self.data_initialized = False
            self.root.after(0, lambda: self.status_var.set(f"指数更新失败: {str(e)}"))
    
    def initial_load(self):
        """初始加载数据"""
        if self.validate_dates(show_error=False):
            self.trigger_analysis()

    def validate_dates(self, show_error=True):
        """日期有效性验证"""
        try:
            start = self.start_date_picker.get_date()
            end = self.end_date_picker.get_date()
            
            if not start or not end:
                if show_error: messagebox.showerror("错误", "请选择有效日期")
                return False
                
            if start >= end:
                if show_error: messagebox.showerror("错误", "开始日期必须早于结束日期")
                return False
                
            return True
        except Exception as e:
            if show_error: messagebox.showerror("日期错误", str(e))
            return False
    
    def trigger_analysis(self):
        """触发分析前的验证"""
        if not self.validate_dates(show_error=True):
            return
            
        current_params = (
            self.index_var.get(),
            self.start_date_picker.get_date().strftime("%Y%m%d"),
            self.end_date_picker.get_date().strftime("%Y%m%d")
        )
        
        if current_params == self.last_update_params:
            return
            
        self.last_update_params = current_params
        self.start_full_analysis()
    
    def start_full_analysis(self):
        """更新后的分析方法（完全移除按钮状态控制）"""
        try:
            start_date = self.start_date_picker.get_date()
            end_date = self.end_date_picker.get_date()
            
            analysis_thread = threading.Thread(
                target=self.perform_full_analysis,
                args=(start_date, end_date),
                daemon=True
            )
            analysis_thread.start()
        except Exception as e:
            self.status_var.set(f"分析启动失败: {str(e)}")
            self.logger.log(f"分析启动错误: {traceback.format_exc()}")
    
    def create_overview_cards(self):
        """创建数据分析概览卡片（修改后版本）"""
        # 第一行：相关性分析卡片
        self.north_card = ttk.LabelFrame(self.overview_frame, text="北向资金相关性", padding="10")
        self.north_card.grid(row=0, column=0, padx=5, pady=5, sticky=tk.NSEW)
        
        self.main_card = ttk.LabelFrame(self.overview_frame, text="主力资金相关性", padding="10")
        self.main_card.grid(row=0, column=1, padx=5, pady=5, sticky=tk.NSEW)
        
        self.fund_card = ttk.LabelFrame(self.overview_frame, text="资金协同性", padding="10")
        self.fund_card.grid(row=0, column=2, padx=5, pady=5, sticky=tk.NSEW)
        
        self.turnover_card = ttk.LabelFrame(self.overview_frame, text="成交额相关性", padding="10")
        self.turnover_card.grid(row=0, column=3, padx=5, pady=5, sticky=tk.NSEW)

        # 第二行：资金比率相关性卡片
        self.main_ratio_card = ttk.LabelFrame(self.overview_frame, text="主力资金比率相关性", padding="10")
        self.main_ratio_card.grid(row=1, column=0, padx=5, pady=5, sticky=tk.NSEW)
        
        self.north_ratio_card = ttk.LabelFrame(self.overview_frame, text="北向资金比率相关性", padding="10")
        self.north_ratio_card.grid(row=1, column=1, padx=5, pady=5, sticky=tk.NSEW)
        
        # 第二行：统计信息卡片（占用两列）
        self.main_stats_card = ttk.LabelFrame(self.overview_frame, text="主力资金统计", padding="10")
        self.main_stats_card.grid(row=1, column=2, columnspan=2, padx=5, pady=5, sticky=tk.NSEW)
        
        # 第三行：成交额统计卡片（占用四列）
        self.turnover_stats_card = ttk.LabelFrame(self.overview_frame, text="成交额统计", padding="10")
        self.turnover_stats_card.grid(row=2, column=0, columnspan=4, padx=5, pady=5, sticky=tk.NSEW)

        # === 主力资金统计内容 ===
        stats_grid = ttk.Frame(self.main_stats_card)
        stats_grid.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 左列：天数统计
        ttk.Label(stats_grid, textvariable=self.main_positive_days, 
                font=("SimHei", 11)).grid(row=0, column=0, sticky=tk.W, padx=5)
        ttk.Label(stats_grid, textvariable=self.main_monthly_positive,
                font=("SimHei", 11)).grid(row=1, column=0, sticky=tk.W, padx=5)
        
        # 右列：资金流量
        ttk.Label(stats_grid, textvariable=self.main_max_inflow,
                font=("SimHei", 11)).grid(row=0, column=1, sticky=tk.W, padx=5)
        ttk.Label(stats_grid, textvariable=self.main_max_outflow,
                font=("SimHei", 11)).grid(row=1, column=1, sticky=tk.W, padx=5)
        ttk.Label(stats_grid, textvariable=self.main_avg_inflow,
                font=("SimHei", 11)).grid(row=2, column=1, sticky=tk.W, padx=5)
        ttk.Label(stats_grid, textvariable=self.main_total_inflow,
                font=("SimHei", 11)).grid(row=3, column=1, sticky=tk.W, padx=5)        

        # === 成交额统计内容 ===
        ttk.Label(self.turnover_stats_card, textvariable=self.max_turnover,
                font=("SimHei", 11)).pack(fill=tk.X, pady=2)
        ttk.Label(self.turnover_stats_card, textvariable=self.min_turnover,
                font=("SimHei", 11)).pack(fill=tk.X, pady=2)
        ttk.Label(self.turnover_stats_card, textvariable=self.avg_turnover,
                font=("SimHei", 11)).pack(fill=tk.X, pady=2)
        ttk.Label(self.turnover_stats_card, textvariable=self.total_turnover,
                font=("SimHei", 11)).pack(fill=tk.X, pady=2)
        ttk.Label(self.turnover_stats_card, textvariable=self.turnover_current,
                font=("SimHei", 11)).pack(fill=tk.X, pady=2)
        
        # === 为比率卡片添加标签 ===
        # 主力资金比率卡片
        self.main_ratio_value = tk.StringVar(value="--")
        ttk.Label(self.main_ratio_card, textvariable=self.main_ratio_value, 
                 font=("SimHei", 14, "bold")).pack(expand=True)
        
        # 北向资金比率卡片
        self.north_ratio_value = tk.StringVar(value="--")
        ttk.Label(self.north_ratio_card, textvariable=self.north_ratio_value, 
                 font=("SimHei", 14, "bold")).pack(expand=True)
        

    def _update_main_stats(self, data):
        """更新主力资金统计数据（新增函数）"""
        try:
            # 数据校验
            if 'main_net_inflow' not in data.columns or data.empty:
                self._reset_main_stats()
                return
            
            valid_main = data['main_net_inflow'].dropna()
            if valid_main.empty:
                self._reset_main_stats()
                return

            # 计算正流入天数
            positive_mask = valid_main > 0
            positive_days = valid_main[positive_mask]
            total_positive = len(positive_days)
            
            # 计算月均正天数
            if total_positive > 0:
                monthly_positive = data.loc[positive_days.index].resample('ME', on='date').size()
                avg_monthly = monthly_positive.mean()
            else:
                avg_monthly = 0.0

            # 计算资金流量指标
            max_inflow = valid_main.max()
            min_inflow = valid_main.min()
            avg_inflow = valid_main.mean()
            total_inflow = valid_main.sum()

            # 更新界面变量
            self.main_positive_days.set(f"净流入正天数: {total_positive}")
            self.main_monthly_positive.set(f"月均正天数: {avg_monthly:.1f}")
            self.main_max_inflow.set(f"最大净流入: {max_inflow:.2f} 亿")
            self.main_max_outflow.set(f"最大净流出: {min_inflow:.2f} 亿")
            self.main_avg_inflow.set(f"平均净流入: {avg_inflow:.2f} 亿")
            self.main_total_inflow.set(self._format_total_inflow(total_inflow))

        except Exception as e:
            self.logger.log(f"主力资金统计更新失败: {str(e)}")
            self._reset_main_stats()

    def _format_total_inflow(self, total):
        """智能格式化总金额（新增辅助函数）"""
        if abs(total) >= 10000:  # 超过1万亿转换单位
            return f"总净流入: {total / 10000:.2f} 万亿"
        return f"总净流入: {total:.2f} 亿"

    def _reset_main_stats(self):
        """重置统计显示（新增辅助函数）"""
        self.main_positive_days.set("净流入正天数: --")
        self.main_monthly_positive.set("月均正天数: --")
        self.main_max_inflow.set("最大净流入: --")
        self.main_max_outflow.set("最大净流出: --")
        self.main_avg_inflow.set("平均净流入: --")
        self.main_total_inflow.set("总净流入: --")

    # def update_overview_cards(self):
    #     """更新概览卡片显示（修复版）"""
    #     # 主力资金相关性
    #     if 'main' in self.correlations and self.correlations['main'] is not None:
    #         corr = self.correlations['main']
    #         self.main_value.set(f"{corr:.4f}")
    #         style = "Positive.TLabelframe" if corr > 0.3 else "Negative.TLabelframe" if corr < -0.3 else "Neutral.TLabelframe"
    #         self.main_stats_card.configure(style=style)

    #     # 北向资金相关性
    #     if self.north_enabled.get() and 'north' in self.correlations and self.correlations['north'] is not None:
    #         corr = self.correlations['north']
    #         self.north_value.set(f"{corr:.4f}")
    #         style = "Positive.TLabelframe" if corr > 0.3 else "Negative.TLabelframe" if corr < -0.3 else "Neutral.TLabelframe"
    #         self.north_card.configure(style=style)

    #     # 资金协同性
    #     if self.north_enabled.get() and 'fund' in self.correlations and self.correlations['fund'] is not None:
    #         corr = self.correlations['fund']
    #         self.fund_value.set(f"{corr:.4f}")
    #         style = "Positive.TLabelframe" if corr > 0.5 else "Negative.TLabelframe" if corr < -0.5 else "Neutral.TLabelframe"
    #         self.fund_card.configure(style=style)

    #     # 成交额相关性
    #     if 'turnover' in self.correlations and self.correlations['turnover'] is not None:
    #         corr = self.correlations['turnover']
    #         self.turnover_value.set(f"{corr:.4f}")
    #         style = "Positive.TLabelframe" if corr > 0.3 else "Negative.TLabelframe" if corr < -0.3 else "Neutral.TLabelframe"
    #         self.turnover_card.configure(style=style)

    def update_stats(self, data, analyze_north, analyze_main):
        """更新统计信息（完整实现）"""
        # 主力资金统计
        if 'main_net_inflow' in data.columns:
            valid_main = data[data['main_net_inflow'].notna()]
            if valid_main.empty:
                self.max_inflow.set("最大净流入: --")
                self.max_outflow.set("最大净流出: --")
                self.avg_inflow.set("平均净流入: --")
                self.total_inflow.set("总净流入: --")
            else:
                self.max_inflow.set(f"最大净流入: {valid_main['main_net_inflow'].max():.2f}亿")
                self.max_outflow.set(f"最大净流出: {valid_main['main_net_inflow'].min():.2f}亿")
                self.avg_inflow.set(f"平均净流入: {valid_main['main_net_inflow'].mean():.2f}亿")
                self.total_inflow.set(f"总净流入: {valid_main['main_net_inflow'].sum():.2f}亿")

        # 成交额统计
        if 'total_turnover' in data.columns:
            valid_turnover = data[data['total_turnover'].notna()]
            if valid_turnover.empty:
                self.max_turnover.set("最大成交额: --")
                self.min_turnover.set("最小成交额: --")
                self.avg_turnover.set("平均成交额: --")
                self.total_turnover.set("总成交额: --")
            else:
                self.max_turnover.set(f"最大成交额: {valid_turnover['total_turnover'].max():.2f}亿")
                self.min_turnover.set(f"最小成交额: {valid_turnover['total_turnover'].min():.2f}亿")
                self.avg_turnover.set(f"平均成交额: {valid_turnover['total_turnover'].mean():.2f}亿")
                self.total_turnover.set(f"总成交额: {valid_turnover['total_turnover'].sum()/10000:.2f}万亿")    

    def perform_full_analysis(self, start_date, end_date):
        """执行完整分析流程（修复current_data设置时机）"""
        try:
            # ===== 重置数据状态 =====
            self.data_initialized = False
            self.data_source = "perform_full_analysis"
            self.logger.log(f"🚀 开始完整分析流程 (来源: {self.data_source})")
            
            # ===== 日期标准化阶段 =====
            # 处理所有可能的日期输入类型
            def convert_to_timestamp(date_obj):
                """统一转换为 Pandas Timestamp"""
                if isinstance(date_obj, pd.Timestamp):
                    return date_obj
                try:
                    return pd.to_datetime(date_obj)
                except Exception as e:
                    raise ValueError(f"无效的日期格式: {date_obj}") from e

            start_date = convert_to_timestamp(start_date)
            end_date = convert_to_timestamp(end_date)
            
            self.logger.log(f"📅 分析日期范围: {start_date.strftime('%Y-%m-%d')} 至 {end_date.strftime('%Y-%m-%d')}")

            # ===== 数据获取阶段 =====
            self.root.after(0, lambda: self.status_var.set("🚀 正在加载指数数据..."))
            index_code = self.index_var.get()
            index_data = self.data_fetcher.get_market_index_data(index_code, start_date, end_date)
            
            # 强化空数据校验（带日期范围提示）
            if index_data.empty:
                date_range = f"{start_date.strftime('%Y-%m-%d')} 至 {end_date.strftime('%Y-%m-%d')}"
                error_msg = f"📊 指数 {index_code} 在以下范围无数据: \n{date_range}"
                self.logger.log(error_msg)
                raise ValueError(error_msg)
            else:
                self.logger.log(f"✅ 获取指数数据成功: {len(index_data)} 条记录")

            # ===== 数据加载阶段 =====
            # 主力资金数据（带重试机制）
            self.root.after(0, lambda: self.status_var.set("🔄 正在加载主力资金数据..."))
            main_data = self.data_fetcher.get_main_money_flow_data(start_date, end_date)
            self.logger.log(f"📊 主力资金数据: {'已获取' if main_data is not None else '未获取'}")
            
            # 北向资金数据（条件加载）
            north_data = None
            if self.north_enabled.get():
                self.root.after(0, lambda: self.status_var.set("🌐 正在加载北向资金数据..."))
                north_data = self.data_fetcher.get_north_flow_data(start_date, end_date)
                self.logger.log(f"📊 北向资金数据: {'已获取' if north_data is not None else '未获取'}")
            else:
                self.logger.log("ℹ️ 北向资金分析已禁用")

            # 成交额数据（严格校验）
            self.root.after(0, lambda: self.status_var.set("💹 正在加载市场成交额..."))
            turnover_data = self.data_fetcher.get_market_turnover(start_date, end_date)
            if 'total_turnover' not in turnover_data.columns:
                self.logger.log("⚠️ 成交额数据缺失关键列，使用备份数据")
                turnover_data = self._get_backup_turnover_data(start_date, end_date)
            self.logger.log(f"📊 成交额数据: {len(turnover_data)} 条记录")

            # ===== 数据合并阶段 =====
            self.root.after(0, lambda: self.status_var.set("🔗 正在合并数据集..."))
            merged_data = self.merge_data(
                index_data=index_data,
                north_data=north_data,
                main_data=main_data,
                analyze_north=self.north_enabled.get(),
                analyze_main=('main_net_inflow' in main_data.columns) if main_data is not None else False,
                turnover_data=turnover_data
            )
            
            self.logger.log(f"📊 合并后数据集: {len(merged_data)} 条记录")

            # ===== 分析计算阶段 =====
            self.root.after(0, lambda: self.status_var.set("🧮 正在计算相关性..."))
            correlations = self.calculate_correlations(
                merged_data,
                analyze_north=self.north_enabled.get(),
                analyze_main=('main_net_inflow' in merged_data.columns)
            )
            
            # 记录相关性结果
            for key, value in correlations.items():
                self.logger.log(f"📈 {key}相关性: {value:.4f}")

            # ===== 关键修复点：提前设置current_data =====
            self.current_data = merged_data
            self.data_initialized = True
            self.data_source = "perform_full_analysis"
            self.data_last_update = datetime.now()
            self.logger.log(f"✅ 基础数据已设置 (大小: {len(merged_data)})")
            
            # ===== 界面更新阶段 =====
            self.root.after(0, lambda: self.full_update(
                merged_data=merged_data,
                correlations=correlations,
                analyze_north=self.north_enabled.get(),
                analyze_main=('main_net_inflow' in merged_data.columns)
            ))

            # ===== 高级分析阶段 =====
            if not merged_data.empty:
                self.root.after(0, lambda: self.status_var.set("📈 正在分析交割日影响..."))
                self.analyze_delivery_impact()

            self.root.after(0, lambda: self.status_var.set(
                f"✅ 分析完成: {start_date.strftime('%Y-%m-%d')} → {end_date.strftime('%Y-%m-%d')}"
            ))

        # ===== 异常处理阶段 =====
        except pd.errors.OutOfBoundsDatetime as e:
            error_msg = f"📅 日期越界错误: {str(e)}"
            self.root.after(0, lambda: self.status_var.set(error_msg))
            self.logger.log(f"🛑 日期异常详情:\n{traceback.format_exc()}")
            self.data_initialized = False

        except ValueError as e:
            self.root.after(0, lambda: self.status_var.set(f"❌ {str(e)}"))
            self.logger.log(f"🛑 校验异常详情:\n{traceback.format_exc()}")
            self.data_initialized = False

        except Exception as e:
            error_msg = f"⚠️ 未捕获异常: {str(e)}"
            self.root.after(0, lambda: self.status_var.set(error_msg))
            self.logger.log(f"🛑 系统异常详情:\n{traceback.format_exc()}")
            self.data_initialized = False

        finally:
            # ===== 资源清理阶段 =====
            self._clean_temp_data()
            self.root.after(0, lambda: self.status_var.set("🟢 系统就绪"))
            self.logger.log(f"🔚 分析流程结束 (状态: {'成功' if self.data_initialized else '失败'})")

    def full_update(self, merged_data, correlations, analyze_north, analyze_main):
        """完整界面更新（修正参数签名）"""
        try:
            # 更新指数趋势图
            self.plot_index_trend(merged_data, self.index_var.get())
            
            # 更新资金流向趋势图（传递参数）
            self.plot_fund_flow_trend(
                merged_data,
                analyze_north=analyze_north,
                analyze_main=analyze_main
            )
            
            # 更新成交额趋势图
            self.plot_turnover_trend(merged_data)
            
            # 更新相关性散点图（传递参数）
            self.plot_correlation_scatter(
                merged_data,
                analyze_north=analyze_north,
                analyze_main=analyze_main
            )
            
            # 更新数据表格
            self.update_data_table(merged_data)
            
            # 更新概览卡片（传递参数）
            self.update_overview_cards(
                correlations,
                analyze_north=analyze_north,
                analyze_main=analyze_main
            )
            
            self.status_var.set(f"界面更新完成（{len(merged_data)}个交易日）")
            
        except Exception as e:
            self.logger.log(f"完整更新失败: {str(e)}")
            messagebox.showerror("更新错误", f"界面更新失败: {str(e)}")
    
    
    
    def _get_cached_turnover_data(self, start_date: date, end_date: date) -> pd.DataFrame:
        """尝试从现有缓存中获取部分或全部成交额数据"""
        # 查找包含在请求范围内的缓存项
        overlapping_caches = []
        for (cache_start, cache_end), cache_data in self.turnover_cache.items():
            if (cache_start <= end_date) and (cache_end >= start_date):
                overlapping_caches.append((cache_start, cache_end, cache_data))
        
        if not overlapping_caches:
            return None
            
        # 合并所有重叠的缓存数据
        all_cached_data = []
        for cache_start, cache_end, cache_data in overlapping_caches:
            # 截取在请求范围内的数据
            cache_in_range = cache_data[
                (pd.to_datetime(cache_data['date']).dt.date >= start_date) & 
                (pd.to_datetime(cache_data['date']).dt.date <= end_date)
            ]
            all_cached_data.append(cache_in_range)
        
        if not all_cached_data:
            return None
            
        # 合并并去重
        merged = pd.concat(all_cached_data, ignore_index=True)
        merged = merged.sort_values('date').drop_duplicates('date')
        
        return merged
    
    def _fetch_missing_turnover_data(self, start_date: date, end_date: date) -> pd.DataFrame:
        """获取缺失的成交额数据"""
        # 原有的获取成交额数据的核心逻辑
        trade_dates = self.trading_day_checker.get_trading_days(start_date, end_date)
        dates_str = [d.strftime("%Y%m%d") for d in trade_dates]

        
        sse_data = []
        szse_data = []
        
        total_dates = len(dates_str)
        for i, date_str in enumerate(dates_str):
            self.logger.log(f"正在获取{date_str}成交额数据 ({i+1}/{total_dates})")
            
            sse = self.data_fetcher.fetch_sse_turnover(date_str)
            szse = self.data_fetcher.fetch_szse_turnover(date_str)
            
            sse_data.append((date_str, sse))
            szse_data.append((date_str, szse))
        
        # 转换为DataFrame
        sse_df = pd.DataFrame(sse_data, columns=['date_str', 'sse_turnover'])
        szse_df = pd.DataFrame(szse_data, columns=['date_str', 'szse_turnover'])
        
        # 合并数据
        merged = pd.merge(sse_df, szse_df, on='date_str', how='outer')
        merged['date'] = pd.to_datetime(merged['date_str'])
        merged['total_turnover'] = merged['sse_turnover'] + merged['szse_turnover']
        
        return merged[['date', 'sse_turnover', 'szse_turnover', 'total_turnover']]
    
    # def _fetch_full_turnover_data(self, start_date: date, end_date: date) -> pd.DataFrame:
    #     """获取完整成交额数据（修复变量初始化问题）"""
    #     try:
    #         # 初始化数据容器
    #         sse_data = []
    #         szse_data = []
    #         dates_str = []

    #         # 获取交易日列表
    #         trade_dates = self.trading_day_checker.get_trading_days(start_date, end_date)
    #         if not trade_dates:
    #             self.logger.log(f"无交易日数据 {start_date} 至 {end_date}")
    #             return pd.DataFrame(columns=['date', 'sse_turnover', 'szse_turnover', 'total_turnover'])

    #         # 准备日期列表
    #         dates_str = [d.strftime("%Y%m%d") for d in trade_dates]

    #         # 获取交易所数据
    #         for date_str in dates_str:
    #             try:
    #                 sse = self.fetch_sse_turnover(date_str)
    #                 szse = self.fetch_szse_turnover(date_str)
    #                 sse_data.append(sse)
    #                 szse_data.append(szse)
    #             except Exception as e:
    #                 self.logger.log(f"获取 {date_str} 数据失败: {str(e)}")
    #                 sse_data.append(0.0)
    #                 szse_data.append(0.0)

    #         # 创建DataFrame（确保初始化）
    #         df = pd.DataFrame({
    #             'date_str': dates_str,
    #             'sse_turnover': sse_data,
    #             'szse_turnover': szse_data
    #         })

    #         # 转换日期类型
    #         if not df.empty:
    #             df['date'] = pd.to_datetime(df['date_str'], format='%Y%m%d')
    #             df = df.drop(columns=['date_str'])
    #             df['total_turnover'] = df['sse_turnover'] + df['szse_turnover']
    #             print(df['total_turnover'])
    #             print(df['total_turnover'])
    #             print(df['szse_turnover'])
    #             df = df.sort_values('date')
    #         else:
    #             df = pd.DataFrame(columns=['date', 'sse_turnover', 'szse_turnover', 'total_turnover'])

    #         # 过滤日期范围
    #         mask = (df['date'] >= pd.Timestamp(start_date)) & (df['date'] <= pd.Timestamp(end_date))
    #         return df.loc[mask]

    #     except Exception as e:
    #         import traceback
    #         self.logger.log(f"成交额获取失败详情:\n{traceback.format_exc()}")
    #         return pd.DataFrame(columns=['date', 'sse_turnover', 'szse_turnover', 'total_turnover'])
        
    def merge_data(self, index_data, north_data, main_data, analyze_north, analyze_main, turnover_data):
        """合并数据集（日期类型严格校验版）"""
        # ===== 基础数据准备 =====
        merged = index_data[['date', 'close', 'pct_change']].copy()
        merged['date'] = pd.to_datetime(merged['date'])  # 强制类型统一
        
        # ===== 合并成交额数据 =====
        if turnover_data is not None and not turnover_data.empty:
            turnover_data['date'] = pd.to_datetime(turnover_data['date'])
            merged = pd.merge(
                merged,
                turnover_data[['date', 'total_turnover']],
                on='date',
                how='left'
            )
        else:
            merged['total_turnover'] = 0.0  # 填充默认值
        
        # ===== 合并主力资金数据 =====
        if analyze_main and main_data is not None:
            main_data['date'] = pd.to_datetime(main_data['date'])
            merged = pd.merge(
                merged,
                main_data[['date', 'main_net_inflow']],
                on='date',
                how='left'
            )
        
        # ===== 合并北向资金数据 =====
        if analyze_north and north_data is not None:
            north_data['date'] = pd.to_datetime(north_data['date'])
            merged = pd.merge(
                merged,
                north_data[['date', 'north_net_inflow']],
                on='date',
                how='left'
            )
        
        # ===== 最终校验 =====
        required_columns = ['date', 'close', 'pct_change', 'total_turnover']
        missing = [col for col in required_columns if col not in merged.columns]
        if missing:
            raise ValueError(f"合并数据缺失关键列：{missing}")
            
        return merged.sort_values('date').drop_duplicates('date')
    
    def calculate_correlations(self, data, analyze_north, analyze_main):
        """计算资金/成交额比值与指数涨跌幅的相关性"""
        correlations = {}
        
        # 计算主力资金/成交额比值与指数涨跌幅的相关性
        if 'main_net_inflow' in data.columns and 'total_turnover' in data.columns and 'pct_change' in data.columns:
            data['main_ratio'] = data['main_net_inflow'] / data['total_turnover']
            main_ratio_corr = data['main_ratio'].corr(data['pct_change'])
            correlations['main_ratio'] = main_ratio_corr
        
        # 计算北向资金/成交额比值与指数涨跌幅的相关性
        if analyze_north and 'north_net_inflow' in data.columns and 'total_turnover' in data.columns and 'pct_change' in data.columns:
            data['north_ratio'] = data['north_net_inflow'] / data['total_turnover']
            north_ratio_corr = data['north_ratio'].corr(data['pct_change'])
            correlations['north_ratio'] = north_ratio_corr
        """计算各种资金流向与指数涨跌幅的相关系数"""
        correlations = {}
        
        try:
            # 新增：计算主力资金比率（主力资金/成交额）与涨跌幅的相关性
            if 'main_net_inflow' in data.columns and 'total_turnover' in data.columns and 'pct_change' in data.columns:
                # 避免除零，将成交额为零的替换为NaN（这些行会被忽略）
                ratio_main = data['main_net_inflow'] / data['total_turnover'].replace(0, np.nan)
                # 计算相关性，忽略NaN
                main_ratio_corr = ratio_main.corr(data['pct_change'])
                correlations['main_ratio'] = main_ratio_corr
                self.logger.log(f"主力资金比率与指数涨跌幅的相关系数: {main_ratio_corr:.4f}")

            # 新增：计算北向资金比率（北向资金/成交额）与涨跌幅的相关性
            if analyze_north and 'north_net_inflow' in data.columns and 'total_turnover' in data.columns and 'pct_change' in data.columns:
                ratio_north = data['north_net_inflow'] / data['total_turnover'].replace(0, np.nan)
                north_ratio_corr = ratio_north.corr(data['pct_change'])
                correlations['north_ratio'] = north_ratio_corr
                self.logger.log(f"北向资金比率与指数涨跌幅的相关系数: {north_ratio_corr:.4f}")
  

            # 计算主力资金与指数涨跌幅的相关性
            if 'main_net_inflow' in data.columns and 'pct_change' in data.columns:
                main_corr = data['main_net_inflow'].corr(data['pct_change'])
                correlations['main'] = main_corr
                self.logger.log(f"主力资金与指数涨跌幅的相关系数: {main_corr:.4f}")
            
            # 计算北向资金与指数涨跌幅的相关性
            if analyze_north and 'north_net_inflow' in data.columns and 'pct_change' in data.columns:
                north_corr = data['north_net_inflow'].corr(data['pct_change'])
                correlations['north'] = north_corr
                self.logger.log(f"北向资金与指数涨跌幅的相关系数: {north_corr:.4f}")
            
            # 计算北向资金与主力资金的相关性
            if analyze_north and 'north_net_inflow' in data.columns and 'main_net_inflow' in data.columns:
                fund_corr = data['north_net_inflow'].corr(data['main_net_inflow'])
                correlations['fund'] = fund_corr
                self.logger.log(f"北向资金与主力资金的相关系数: {fund_corr:.4f}")
            
            # 计算成交额与指数涨跌幅的相关性
            if 'total_turnover' in data.columns and 'pct_change' in data.columns:
                turnover_corr = data['total_turnover'].corr(data['pct_change'])
                correlations['turnover'] = turnover_corr
                self.logger.log(f"成交额与指数涨跌幅的相关系数: {turnover_corr:.4f}")
            
            return correlations
        except Exception as e:
            self.logger.log(f"计算相关系数时出错: {e}")
            return correlations
    
    def plot_index_trend(self, data, index_name):
        """绘制指数趋势图"""
        self.index_ax.clear()
        
        # 指数名称映射
        index_names = {
            "sh000001": "上证指数",
            "sz399001": "深证成指",
            "sz399006": "创业板指",
            "sh000688": "科创50",
            "sh000300": "沪深300"
        }
        
        # 获取中文名称
        index_name = index_names.get(index_name, index_name)
        
        # 绘制收盘价
        self.index_ax.plot(data['date'], data['close'], color='blue', linewidth=2, label='收盘价')
        
        # 添加涨跌标记
        for i in range(1, len(data)):
            if data.iloc[i]['close'] > data.iloc[i-1]['close']:

                # 添加交割日标记
                if not data.empty:
                    start_date = data['date'].min().date()
                    end_date = data['date'].max().date()
                    delivery_dates = self.delivery_calculator.get_monthly_delivery_dates(start_date, end_date)
                    
                    for delivery_date in delivery_dates:
                        delivery_datetime = datetime.combine(delivery_date, datetime.min.time())
                        if delivery_datetime >= data['date'].min() and delivery_datetime <= data['date'].max():
                            self.index_ax.axvline(x=delivery_datetime, color='orange', linestyle='--', linewidth=1, alpha=0.7)
                            self.index_ax.text(delivery_datetime, self.index_ax.get_ylim()[1]*0.95,
                                            '交割日', rotation=90, color='orange', 
                                            verticalalignment='top', alpha=0.7)
                self.index_ax.scatter(data.iloc[i]['date'], data.iloc[i]['close'], color='red', marker='^', s=50)
            elif data.iloc[i]['close'] < data.iloc[i-1]['close']:
                self.index_ax.scatter(data.iloc[i]['date'], data.iloc[i]['close'], color='green', marker='v', s=50)
        
        # 设置图表标题和标签
        self.index_ax.set_title(f"{index_name}趋势图", fontsize=14)
        self.index_ax.set_xlabel("日期")
        self.index_ax.set_ylabel("指数")
        
        # 添加网格线
        self.index_ax.grid(True, linestyle='--', alpha=0.7)
        
        # 添加图例
        self.index_ax.legend(loc='upper left')
        
        # 设置日期格式化
        from matplotlib.dates import DateFormatter
        date_format = DateFormatter('%m-%d')
        self.index_ax.xaxis.set_major_formatter(date_format)
        
        # 自动调整布局
        self.index_fig.tight_layout()
        self.index_canvas.draw()
    
    def plot_fund_flow_trend(self, data, analyze_north=True, analyze_main=True):
        """绘制资金流向趋势图（完整实现）"""
        self.fund_flow_ax.clear()
        ax = self.fund_flow_ax
        
        try:
            # 主力资金柱状图（带颜色区分）
            if analyze_main and 'main_net_inflow' in data.columns:
                # 生成颜色列表（红涨绿跌）
                colors = np.where(data['main_net_inflow'] >= 0, '#FF4444', '#44FF44')
                bars = ax.bar(data['date'], data['main_net_inflow'], 
                            width=0.8, color=colors, alpha=0.7,
                            label='主力资金净流入')
                
                # 添加统计信息标注
                valid_main = data['main_net_inflow'].dropna()
                if not valid_main.empty:                   
                    self._update_main_stats(data)

                # 添加悬停提示
                self.add_main_money_tooltip(bars, data)
        

            # 北向资金折线图（带动态颜色）
            if analyze_north and 'north_net_inflow' in data.columns:
                ax2 = ax.twinx()
                # 生成动态颜色（绿涨红跌）
                colors = np.where(data['north_net_inflow'] >= 0, '#00AA00', '#AA0000')
                
                # 分段绘制不同颜色的线段
                for i in range(1, len(data)):
                    ax2.plot(data['date'].iloc[i-1:i+1], 
                            data['north_net_inflow'].iloc[i-1:i+1],
                            color=colors[i], linewidth=2, label='北向资金净流入')
                
                # 避免重复标签
                handles, labels = ax2.get_legend_handles_labels()
                if handles:
                    ax2.legend([handles[0]], [labels[0]], loc='upper right')

                # 添加统计信息标注
                valid_north = data['north_net_inflow'].dropna()
                if not valid_north.empty:
                    stats_text = (
                        f"北向资金统计\n"
                        f"最大流入: {valid_north.max():.2f}亿\n"
                        f"最大流出: {valid_north.min():.2f}亿\n"
                        f"平均值: {valid_north.mean():.2f}亿"
                    )
                    ax2.text(0.98, 0.75, stats_text, transform=ax.transAxes,
                            ha='right', va='top', 
                            bbox=dict(facecolor='white', alpha=0.8, edgecolor='gray'))
                
                ax2.set_ylabel('北向资金 (亿元)', color='#555555')
                ax2.tick_params(axis='y', labelcolor='#555555')

            # 通用图表设置
            ax.set_title("资金流向趋势分析", fontsize=14, pad=20)
            ax.set_xlabel("日期", fontsize=12)
            ax.set_ylabel("主力资金 (亿元)", color='#1f77b4')
            ax.tick_params(axis='y', labelcolor='#1f77b4')
            
            # 日期格式化
            ax.xaxis.set_major_formatter(DateFormatter('%m-%d'))
            plt.setp(ax.get_xticklabels(), rotation=45, ha='right')
            
            # 网格和样式
            ax.grid(True, linestyle='--', alpha=0.7, axis='y')
            ax.set_axisbelow(True)
            
            # 合并图例
            lines, labels = ax.get_legend_handles_labels()
            if analyze_north and 'north_net_inflow' in data.columns:
                lines2, labels2 = ax2.get_legend_handles_labels()
                lines += lines2
                labels += labels2
            if lines:
                ax.legend(lines, labels, loc='upper left', 
                        bbox_to_anchor=(0.02, 0.98), framealpha=0.9)

            self.fund_flow_fig.tight_layout()
            self.fund_flow_canvas.draw()

        except KeyError as e:
            self.logger.log(f"绘制资金流向趋势图失败，缺失关键列: {str(e)}")
        except Exception as e:
            self.logger.log(f"绘制资金流向趋势图时发生未知错误: {str(e)}")

    
    def add_main_money_tooltip(self, bars, data):
        """为主力资金柱状图添加悬停提示（修复边缘提示框问题）"""
        if not bars or data.empty:
            return
            
        # 创建标注对象
        annot = self.fund_flow_ax.annotate("", xy=(0,0), xytext=(20,20),
                            textcoords="offset points",
                            bbox=dict(boxstyle="round", fc="w", alpha=0.9),
                            arrowprops=dict(arrowstyle="->"))
        annot.set_visible(False)
        
        # 缓存数据
        self.tooltip_data = {
            'dates': data['date'].dt.strftime('%Y-%m-%d').values,
            'values': data['main_net_inflow'].values
        }
        
        def update_annot(event, bar, idx):
            """更新标注内容（动态调整位置）"""
            # 获取柱子位置
            x, y = bar.get_x() + bar.get_width()/2, bar.get_y() + bar.get_height()
            annot.xy = (x, y)
            
            # 获取图表尺寸
            ax_width = self.fund_flow_ax.get_window_extent().width
            ax_height = self.fund_flow_ax.get_window_extent().height
            
            # 获取柱子位置在屏幕坐标中的位置
            bar_screen = self.fund_flow_ax.transData.transform((x, y))
            fig_screen = self.fund_flow_fig.transFigure.inverted().transform(bar_screen)
            
            # 动态调整提示框位置
            if fig_screen[0] > 0.8:  # 如果柱子靠近右侧边缘
                xytext = (-120, 20)  # 向左移动提示框
            elif fig_screen[0] < 0.2:  # 如果柱子靠近左侧边缘
                xytext = (120, 20)  # 向右移动提示框
            elif fig_screen[1] < 0.2:  # 如果柱子靠近底部边缘
                xytext = (0, 80)  # 向上移动提示框
            else:  # 默认位置
                xytext = (20, 20)
                
            # 设置提示框内容
            date = self.tooltip_data['dates'][idx]
            value = self.tooltip_data['values'][idx]
            annot.set_text(f"日期: {date}\n净流入: {value:.2f}亿")
            annot.xyann = xytext
            
            # 设置背景透明度
            annot.get_bbox_patch().set_alpha(0.9)
        
        def hover(event):
            """鼠标悬停事件处理"""
            vis = annot.get_visible()
            if event.inaxes == self.fund_flow_ax:
                for i, bar in enumerate(bars):
                    cont, idx = bar.contains(event)
                    if cont:
                        update_annot(event, bar, i)
                        annot.set_visible(True)
                        self.fund_flow_fig.canvas.draw_idle()
                        return
                if vis:
                    annot.set_visible(False)
                    self.fund_flow_fig.canvas.draw_idle()
        
        # 绑定事件
        self.fund_flow_fig.canvas.mpl_connect("motion_notify_event", hover)


    def plot_turnover_trend(self, turnover_data):
        """绘制成交额趋势图（独立数据版本）"""
        self.turnover_ax.clear()
        
        if turnover_data.empty or 'total_turnover' not in turnover_data.columns:
            self.turnover_ax.set_title("无有效数据")
            self.turnover_canvas.draw()
            return
        
        try:
            # 绘制柱状图
            self.turnover_ax.bar(
                turnover_data['date'], 
                turnover_data['total_turnover'],
                color='#1f77b4', 
                alpha=0.7,
                label='日成交额'
            )# 标记最大值

            max_idx = turnover_data['total_turnover'].idxmax()
            max_date = turnover_data.loc[max_idx, 'date']
            max_value = turnover_data.loc[max_idx, 'total_turnover']
            self.turnover_ax.scatter(max_date, max_value, 
                                   color='red', s=100, zorder=5,
                                   label=f'最大值: {max_value:,.2f}亿')
            self.turnover_ax.annotate(f'{max_value:,.2f}亿', 
                                    (max_date, max_value),
                                    xytext=(0, 15),
                                    textcoords='offset points',
                                    ha='center', va='bottom',
                                    fontsize=9, color='red')
            
            # 标记最小值
            min_idx = turnover_data['total_turnover'].idxmin()
            min_date = turnover_data.loc[min_idx, 'date']
            min_value = turnover_data.loc[min_idx, 'total_turnover']
            self.turnover_ax.scatter(min_date, min_value, 
                                   color='green', s=100, zorder=5,
                                   label=f'最小值: {min_value:,.2f}亿')

            
            # 添加移动平均线（如果数据足够）
            if len(turnover_data) >= 5:
                ma_window = min(20, len(turnover_data)//2)
                turnover_data['ma'] = turnover_data['total_turnover'].rolling(ma_window).mean()
                self.turnover_ax.plot(
                    turnover_data['date'], 
                    turnover_data['ma'],
                    color='orange', 
                    linewidth=2,
                    label=f'{ma_window}日平均'
                )
            
            # 设置图表属性
            self.turnover_ax.set_title("成交额趋势分析")
            self.turnover_ax.set_xlabel("日期")
            self.turnover_ax.set_ylabel("成交额 (亿元)")
            self.turnover_ax.legend()
            self.turnover_ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d'))
            self.turnover_ax.grid(True, linestyle='--', alpha=0.5)
            
            # 自动旋转日期标签
            plt.setp(self.turnover_ax.get_xticklabels(), rotation=45, ha='right')
            
            self.turnover_fig.tight_layout()
            self.turnover_canvas.draw()
            
        except Exception as e:
            self.logger.log(f"绘制成交额图表失败: {str(e)}")
            self.turnover_ax.set_title("图表渲染错误")
            self.turnover_canvas.draw()
    
    def plot_correlation_scatter(self, data, analyze_north, analyze_main):
        """绘制资金流向与指数涨跌幅相关性散点图（参数修复版）"""
        self.scatter_ax.clear()
 # 检查数据列是否存在，至少需要主力资金和成交额
        if not analyze_main or 'main_net_inflow' not in data.columns or 'total_turnover' not in data.columns or 'pct_change' not in data.columns:
            self.scatter_ax.set_title("数据不足，无法绘制散点图")
            self.scatter_canvas.draw()
            return
        
        # 计算主力资金比率（主力资金/成交额）
        # 避免除零，将成交额为零的替换为NaN（这些行会被忽略）
        data['main_ratio'] = data['main_net_inflow'] / data['total_turnover'].replace(0, np.nan)
        valid_main = data.dropna(subset=['main_ratio', 'pct_change'])
        
        # 绘制主力资金比率散点
        self.scatter_ax.scatter(
            valid_main['main_ratio'], 
            valid_main['pct_change'],
            color='blue', 
            alpha=0.6, 
            label='主力资金比率'
        )
        
        # 计算北向资金比率（如果启用）
        if analyze_north and 'north_net_inflow' in data.columns:
            data['north_ratio'] = data['north_net_inflow'] / data['total_turnover'].replace(0, np.nan)
            valid_north = data.dropna(subset=['north_ratio', 'pct_change'])
            self.scatter_ax.scatter(
                valid_north['north_ratio'], 
                valid_north['pct_change'],
                color='green', 
                alpha=0.6, 
                label='北向资金比率'
            )
        
        # 添加回归线（主力资金比率）
        if not valid_main.empty:
            x_main = valid_main['main_ratio'].values
            y_main = valid_main['pct_change'].values
            m_main, b_main = np.polyfit(x_main, y_main, 1)
            self.scatter_ax.plot(
                x_main, 
                m_main * x_main + b_main, 
                'b-', 
                linewidth=2,
                label='主力比率回归线'
            )
        
        # 添加回归线（北向资金比率）
        if analyze_north and not valid_north.empty:
            x_north = valid_north['north_ratio'].values
            y_north = valid_north['pct_change'].values
            m_north, b_north = np.polyfit(x_north, y_north, 1)
            self.scatter_ax.plot(
                x_north, 
                m_north * x_north + b_north, 
                'g-', 
                linewidth=2,
                label='北向比率回归线'
            )
        
        # 设置图表属性
        self.scatter_ax.set_title('资金比率与指数涨跌幅相关性')
        self.scatter_ax.set_xlabel('资金比率（资金/成交额）')
        self.scatter_ax.set_ylabel('指数涨跌幅 (%)')
        self.scatter_ax.legend()
        self.scatter_fig.tight_layout()
        self.scatter_canvas.draw()

    def _add_regression_line(self, data, column, color):
        """辅助方法：添加回归线"""
        x = data[column].values
        y = data['pct_change'].values
        mask = ~np.isnan(x) & ~np.isnan(y)
        if np.sum(mask) < 2:
            return  # 数据不足不绘制
        
        m, b = np.polyfit(x[mask], y[mask], 1)
        self.scatter_ax.plot(
            x, 
            m * x + b, 
            color, 
            linewidth=2,
            label=f'{column} 回归线 (斜率={m:.4f})'
        )
    
    def update_data_table(self, data):
        """更新数据表格"""
        # 清除现有数据
        for item in self.data_tree.get_children():
            self.data_tree.delete(item)
        
        # 创建带单位的列名
        columns = list(data.columns)
        display_columns = []
        # 添加比值列的单位
        units = {
            'close': '点',
            'pct_change': '%',
            'main_net_inflow': '亿',
            'north_net_inflow': '亿',
            'total_turnover': '亿'
        }
        units['main_ratio'] = '%'  # 主力资金/成交额比值单位
        units['north_ratio'] = '%'  # 北向资金/成交额比值单位
        
        for col in columns:
            if col in units:
                display_columns.append(f"{col} ({units[col]})")
            else:
                display_columns.append(col)
        
        self.data_tree['columns'] = columns
        self.data_tree.heading('#0', text='序号')
        
        # 设置列标题
        for i, col in enumerate(columns):
            # 显示名称带单位
            display_name = display_columns[i]
            self.data_tree.heading(col, text=display_name)
            
            # 根据列类型设置列宽
            if 'date' in col.lower():
                self.data_tree.column(col, width=120)
            elif 'pct_change' in col.lower():
                self.data_tree.column(col, width=80, anchor=tk.E)
            else:
                self.data_tree.column(col, width=100, anchor=tk.E)
        
        # 添加数据
        for i, row in data.iterrows():
            values = [str(row[col]) for col in columns]
            self.data_tree.insert('', 'end', values=values)
    
    def update_overview_cards(self, correlations, analyze_north, analyze_main):
        """更新概览卡片"""
        # 更新主力资金相关性
        if 'main' in correlations:
            self.main_value.set(f"{correlations['main']:.4f}")
            # 根据相关系数设置颜色
            if correlations['main'] > 0.3:
                self.main_card.configure(style="Positive.TLabelframe")
                # 修改这里：获取实际的Label小部件并设置颜色
                for child in self.main_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['main'] < -0.3:
                self.main_card.configure(style="Negative.TLabelframe")
                # 修改这里
                for child in self.main_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_ratio_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.main_card.configure(style="Neutral.TLabelframe")
                # 修改这里
                for child in self.main_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_value:
                        child.configure(foreground="#0000FF")
                        break
        
        # 更新北向资金相关性
        if analyze_north and 'north' in correlations:
            self.north_value.set(f"{correlations['north']:.4f}")
            if correlations['north'] > 0.3:
                self.north_card.configure(style="Positive.TLabelframe")
                # 修改这里
                for child in self.north_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.north_ratio_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['north'] < -0.3:
                self.north_card.configure(style="Negative.TLabelframe")
                # 修改这里
                for child in self.north_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.north_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.north_card.configure(style="Neutral.TLabelframe")
                # 修改这里
                for child in self.north_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.north_value:
                        child.configure(foreground="#000000")
                        break
            self.north_card.grid()  # 显示卡片
        else:
            self.north_card.grid_remove()  # 隐藏卡片
        
        # 更新资金协同性
        if analyze_north and 'fund' in correlations:
            self.fund_value.set(f"{correlations['fund']:.4f}")
            if correlations['fund'] > 0.5:
                self.fund_card.configure(style="Positive.TLabelframe")
                # 修改这里
                for child in self.fund_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.fund_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['fund'] < -0.5:
                self.fund_card.configure(style="Negative.TLabelframe")
                # 修改这里
                for child in self.fund_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.fund_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.fund_card.configure(style="Neutral.TLabelframe")
                # 修改这里
                for child in self.fund_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.fund_value:
                        child.configure(foreground="#000000")
                        break
            self.fund_card.grid()  # 显示卡片
        else:
            self.fund_card.grid_remove()  # 隐藏卡片
        
        # 更新成交额相关性
        # 新增：更新主力资金/成交额比值相关性卡片
        if 'main_ratio' in correlations:
            self.main_ratio_value.set(f"{correlations['main_ratio']:.4f}")
            # 根据相关系数设置颜色
            if correlations['main_ratio'] > 0.3:
                self.main_ratio_card.configure(style="Positive.TLabelframe")
                for child in self.main_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_ratio_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['main_ratio'] < -0.3:
                self.main_ratio_card.configure(style="Negative.TLabelframe")
                for child in self.main_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_ratio_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.main_ratio_card.configure(style="Neutral.TLabelframe")
                for child in self.main_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_ratio_value:
                        child.configure(foreground="#0000FF")
                        break
            self.main_ratio_card.grid()  # 显示卡片
        else:
            self.main_ratio_card.grid_remove()  # 隐藏卡片
        
        # 新增：更新北向资金/成交额比值相关性卡片
        if analyze_north and 'north_ratio' in correlations:
            self.north_ratio_value.set(f"{correlations['north_ratio']:.4f}")
            # 根据相关系数设置颜色（类似上面的逻辑）
            # ...（省略具体实现，与main_ratio_card类似）...
            self.north_ratio_card.grid()  # 显示卡片
        else:
            self.north_ratio_card.grid_remove()  # 隐藏卡片
     
        if 'turnover' in correlations:
            self.turnover_value.set(f"{correlations['turnover']:.4f}")
            if correlations['turnover'] > 0.3:
                self.turnover_card.configure(style="Positive.TLabelframe")
                # 修改这里
                for child in self.turnover_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.turnover_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['turnover'] < -0.3:
                self.turnover_card.configure(style="Negative.TLabelframe")
                # 修改这里
                for child in self.turnover_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.turnover_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.turnover_card.configure(style="Neutral.TLabelframe")
                # 修改这里
                for child in self.turnover_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.turnover_value:
                        child.configure(foreground="#000000")
                        break
            self.turnover_card.grid()  # 显示卡片
        else:
            self.turnover_card.grid_remove()  # 隐藏卡片


                
        # 新增：更新主力资金比率相关性卡片
        if 'main_ratio' in correlations:
            self.main_ratio_value.set(f"{correlations['main_ratio']:.4f}")
            # 根据相关系数设置颜色
            if correlations['main_ratio'] > 0.3:
                self.main_ratio_card.configure(style="Positive.TLabelframe")
                for child in self.main_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_ratio_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['main_ratio'] < -0.3:
                self.main_ratio_card.configure(style="Negative.TLabelframe")
                for child in self.main_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_ratio_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.main_ratio_card.configure(style="Neutral.TLabelframe")
                for child in self.main_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.main_ratio_value:
                        child.configure(foreground="#0000FF")
                        break
            self.main_ratio_card.grid()  # 显示卡片
        else:
            self.main_ratio_card.grid_remove()  # 隐藏卡片
        
        # 新增：更新北向资金比率相关性卡片
        if analyze_north and 'north_ratio' in correlations:
            self.north_ratio_value.set(f"{correlations['north_ratio']:.4f}")
            if correlations['north_ratio'] > 0.3:
                self.north_ratio_card.configure(style="Positive.TLabelframe")
                for child in self.north_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.north_ratio_value:
                        child.configure(foreground="#008000")
                        break
            elif correlations['north_ratio'] < -0.3:
                self.north_ratio_card.configure(style="Negative.TLabelframe")
                for child in self.north_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.north_ratio_value:
                        child.configure(foreground="#FF0000")
                        break
            else:
                self.north_ratio_card.configure(style="Neutral.TLabelframe")
                for child in self.north_ratio_card.winfo_children():
                    if isinstance(child, ttk.Label) and child.cget("textvariable") == self.north_ratio_value:
                        child.configure(foreground="#0000FF")
                        break
            self.north_ratio_card.grid()  # 显示卡片
        else:
            self.north_ratio_card.grid_remove()  # 隐藏卡片
    
    def export_data(self):
        """导出当前数据"""
        if self.current_data is None or self.current_data.empty:
            messagebox.showinfo("导出失败", "没有数据可导出")
            return
        
        try:
            from tkinter import filedialog
            file_path = filedialog.asksaveasfilename(
                defaultextension=".csv",
                filetypes=[("CSV files", "*.csv"), ("Excel files", "*.xlsx")]
            )
            
            if not file_path:
                return
                
            if file_path.endswith('.csv'):
                self.current_data.to_csv(file_path, index=False, encoding='utf-8-sig')
            elif file_path.endswith('.xlsx'):
                self.current_data.to_excel(file_path, index=False)
                
            messagebox.showinfo("导出成功", f"数据已成功导出到 {file_path}")
            self.logger.log(f"数据已导出到 {file_path}")
        except Exception as e:
            messagebox.showerror("导出失败", f"导出数据时出错: {e}")
            self.logger.log(f"导出数据失败: {e}")


if __name__ == "__main__":
    root = tk.Tk()
    app = StockAnalysisApp(root)
    
    # 设置自定义样式
    style = ttk.Style()
    style.configure("Positive.TLabelframe.Label", foreground="#008000")
    style.configure("Negative.TLabelframe.Label", foreground="#FF0000")
    style.configure("Neutral.TLabelframe.Label", foreground="#000000")
    
    root.mainloop()